Saturday, November 15, 2025

New CPAP mask

I received my new Joyce Cara CPAP mask on Wednesday and immediately its effects were felt, reducing the number of apneic events from nearly 20 per hour to only 3 (and there's room for improvement). My original hypothesis - that I discarded - was that the mask needed to be replaced; this is the real reason for the increase in apneic events. The mask came with the straps already attached; all I had to do was tighten them. I think that in previous years I have had to attach the straps myself, and my three left hands mad this process difficult. 

Last year, I wrote that the company was offering the mask for 799 NIS on its website; the price is now 749 NIS. I spoke to a sales person about this and discovered that this was the price of the mask itself, without the flexible pipe that connects to the mask and brings the air from the machine, and without the filters for the machine. I didn't use to change the filters much in the past (although I do now) and I don't know how much the pipe degrades over a year - if at all - but as I'm not paying anyway, it didn't make much sense to try and save. As my wife said when we were talking about some other health issue, don't scrimp on health.

My weight has also reduced by 300 g this week - not a huge amount, but still considerable. The family came over for dinner last night and we talked a little about the diet. I said that I wasn't used to eating at 7 pm and indeed I had stomach ache after dinner. They asked whether I was hungry in the evenings - I said that I frequently feel an urge at tea time for a little something but I can successfully resist this. I don't know what I'm going to do the next time we have a Friday night dinner: either eat much less or else cook a portion of fish and eat that. Friday night communal dinner is one of the great Jewish traditions.

The first rain fell yesterday and woke me up at 4:45 am. Shortly after I got up and took the dog for her morning walk. It started raining again at about 11 am and didn't stop until the evening, so fitting in an afternoon or evening walk was problematic. Before 8 am yesterday, 0.4 mm fell, and until 8 am this morning another 4.9 mm. Not huge figures but it's a start. The rain is supposed to finish by tonight. 

Internal links
[1] 2024
[2] 1853



This day in blog history:

Blog #Date TitleTags
14415/11/2008Adele
30415/11/2010Project Management lecturesMBA, Project management
51915/11/2012Inside the DOCU program (2) - saving and loading rich textProgramming, Delphi
52015/11/2012Inside the DOCU program (3) - Saving screenshotsProgramming, Delphi
90315/11/2015Going to see a man about a dogDog

Monday, November 10, 2025

Implementing CP Prolog - 7A, supplemental

I wrote on Saturday1: I had to take out several of the instrinsic functions that were added last week, so now these have to be restored, and there's no guarantee that the code that worked a week ago will work now because several aspects have been changed and tightened up.

I restored the intrinsic functions necessary for the 'time_in_minutes' query2 on Saturday evening. No syntax errors occurred when I did this, but running a query like ?- time_in_minutes (03:30, X). should give the result 210. Unfortunately the result was 270. Looking at the trace of the query, it was clear what happened. The final goal of the rule is X is H * 60 + M, where X is the result, H the number of hours and M the number of minutes. Instead of calculating (H * 60) + M (i.e. (3*60) + 30), the interpreter was calculating H * (60 + M), or 3 * 90. 

So we had to reimplement the parsing of a line that could have any number of infix operators. CoPilot went through about four different implementations until we finally got something that would work correctly. On and off through the evening, I considered the final implementation and was sure that it was more complex than necessary. There was also a bubble sort at the beginning of the function that could be avoided with a little preplanning (and it's just occurred to me that the bubble sort would run each time the parsing routine was called which is several times: the sort should have been moved out of the function). 

I gave my conclusions to CoPilot and they were sort-of incorporated. At least another two versions were required before the statement was parsed correctly. It's all due to operator precedences  - remember from junior school: multiplication and division before addition and subtraction. Just to make things more complicated, (I quote) In Prolog, operator precedence is a numerical value (typically between 1 and 1200) that determines the order in which operators are evaluated within a term. Higher precedence values indicate weaker binding, meaning operators with higher precedence are considered "further out" in the structure of the term, while lower precedence values indicate stronger binding. For a language of logic, it seems illogical that a higher number has what one would normally call lower precedence. This required several loops to run backwards, although it occurred to me afterwards that it would be more sensible to change the operators table once again so that the loops could run normally. Ultimately it doesn't make any difference.

Once again, I have to say that for all the sophistication of CoPilot, sometimes it makes silly mistakes and sometimes the results aren't what were required. One "endearing" feature that I have noticed is remarks like "there's a problem with your code at such-and-such a place". Sometimes it's typing errors, but more often those are mistakes that CoPilot made in early iterations.

Internal links
[1] 2033
[2] 2029



This day in blog history:

Blog #Date TitleTags
77310/11/2014Literature review: second draft completedDBA
98510/11/2016Data collectionDBA
109010/11/2017INTQUANT and REALQUANTPriority tips
185510/11/2024F25 Cell phone stand with wireless speakerMobile phone

Sunday, November 09, 2025

James D. Watson RIP

James Watson was one of the pair 'Crick and Watson' who elucidated the structure of DNA in 1953. This is, without doubt, one of the defining discoveries in science during the 20th century and created the entire industry of biotech. The advantage that Crick and Watson had was that they were both intellectual magpies, coming to DNA after having worked in other areas. On the other hand, they were fortunate in being the first to unravel the structure; colleagues such as Maurice Wilkins and Rosalind "Rosy" Franklin contributed in major ways.

 In 1967 Watson wrote the book "The double helix" that recounts the story of how Watson started work on DNA, how he met Francis Crick, then Wilkins and Franklin, how they progressed and stumbled and progressed and stumbled. It's illuminating to read this book from the perspective of 2025, especially regarding the inter-personal reactions. All three men were condescending, at the least, to Franklin who made the important contribution of the X-ray photographs. 

I suspect that I read "The double helix" while I was at school, or at least at university. I'm fairly sure that we covered DNA, RNA and protein synthesis as part of the A-level biology curriculum, and this was new science - only twenty years after the discovery. But it wasn't until 2004 that I bought a paperback copy of the book (1999 edition) and I have the 2012 annotated version as an e-book. 

It was this book - or rather, the discovery that the book describes - that opened the way for a great deal of my reading in the past few years. Without C&W (not country and western!), there would have been no "The code breaker" by Walter Isaacson, telling the story of Jennifer Doudna and her work on the CRISPR system of gene editing, or Doudna's own book, "A crack in creation", or any of the biochemistry books by Nick Lane that deal in part with how life could have started and how RNA could have been built initially. I also read a biography of Rosalind Franklin whose name escapes me at the moment. Doudna didn't have good words for Watson who had become controversial in later life.

Gene editing was a common topic in science fiction - fiction that later became facr - as in Robert Silverberg's "Up the line", for example.

An obituary can be read here.



This day in blog history:

Blog #Date TitleTags
42709/11/2011Feeling the pressureMBA, Finance
118509/11/2018Improving a solutionProgramming, Priority tips
143909/11/2021The department of bright ideasProgramming, Problem solving
185409/11/2024Beneath a scarlet skyLiterature, Italy

Saturday, November 08, 2025

Implementing CP Prolog - 7

Calculating the factorial function is the poster boy of recursion. It's very easy to define the factorial of a positive number: if the number is 0 then the factorial is 1, else it's the number itself times the value of the factorial of the number less 1. In other words, the factorial of 1 is 1 * factorial (1 - 1) = 1, and the factorial of 2 is 2. More interestingly, the factorial of 3 is 6 (because that's 3 times 2), and the factorial of 4 is 24 (because that's 4 times 6). This can easily be defined in an imperative language such as Pascal as follows.

function factorial (n: integer): integer; begin if n = 0 then factorial:= 1 else factorial:= n * factorial (n - 1) end;

So simple yet so bad, as calculating the factorial of a number such as 10 will cause a great deal of stack space to be used because of the recursion. An iterative calculation is just as simple and executes with a constant and minimal amount of stack space, as well as being faster.

function factorial (n: integer): integer; var i: integer; begin result:= 1; for i:= 1 to n do result:= result * i; end;

But what does the factorial function look like in Prolog?

factorial (0, 1). factorial (N, F):- N > 0, N1 is N - 1, factorial (N1, F1), F is N * F1.

This matches the initial description that I gave. The factorial of 0 is 1, otherwise assuming that N is greater than 0, the result (F) comes from multiplying the number itself (N) by the factorial of N - 1 (F). The rule looks a bit strange but that's because of the syntax that Prolog requires. 

Last week's installment had the interpreter solving several goals where the final goal included an assignment ('is'), multiplication and addition. A great deal of work was spent on getting that 'is' functor to do what it was intended to do. Calculating the factorial depended on that 'is' functor as it is called twice on every invocation. It turns out that it wasn't correct. 

Before I could tackle the factorial, I wanted to ensure that the 'add' 1 function that I added a few weeks ago still worked. It didn't, so first we had to spend no small amount of time getting this to work again. I knew that 'add' would be a goal on the path of getting the factorial to work.

Let us say that I spent several exceedingly frustrating hours yesterday and today trying to get my interpreter to calculate the factorial. I would run a test and show CoPilot the results. 'Add this', it would return. So I added something more and sometimes it would improve the situation but more often that not would make no change. There are parts of the interpreter (especially handling the 'is' functor) that went through several changes. Sometimes the changes would make things worse, meaning that I had to restore code from previous versions. At one stage, CoPilot said 'do A', then when that didn't work, 'do B', then 'do A'. Exceedingly frustrating. It completely ruined the 'resolve' function that lies at the heart of Prolog, so I had to restore this from last week's code (I back everything up religiously).

CoPilot of course was not running blind. I would run diagnostics and sometimes had enough understanding to see what the current problem was. A key breakthrough was made by me when we saw that that a specific block of code from CP (which had already been tweaked and improved on a previous version) was not doing something that it was supposed to do. That insight almost completely solved the problem. Finally, after having added and removed the same lines time and time again, CP said that You need to ensure that after the rule body succeeds, the rule head variables are unified with the goal arguments. (Does that make any sense? Why wasn't this suggested before?) This was the final piece in solving the puzzle. Now my interpreter can calculate factorials.

In getting this far, I had to take out several of the instrinsic functions that were added last week, so now these have to be restored, and there's no guarantee that the code that worked a week ago will work now because several aspects have been changed and tightened up. It seems that 'maintenance work' (i.e. restoring functions that had been removed to make things clearer) takes more time than actual development work.

I have two more goals to solve (deliberate Prolog speak), although these are not concerned with the external functionality of the interpreter. I want to implement 'garbage collection': in the course of a single query, a certain amount of memory is allocated which is not needed after the query completes and so should be released (and the calculation of factorial (10) will require much more memory than factorial (1)). I know that this doesn't make much difference when one has megabytes of memory, but it's good housekeeping.

In a similar manner, there's a small improvement that should be made in handling rules. Going back to the original family tree example2, there were various functors used, like 'parent', 'male', 'female', 'father', 'mother' and 'grandparent'. If one is trying to find a fact that matches 'male', for example, there should be no need to iterate over all the 17 facts/rules in the database, but only those where the functor is 'male'. I know that this is done by means of a symbol table, where each functor is assigned an index: finding a suitable fact/rule can be done by comparing two index numbers as opposed to invoking the 'expensive' resolution functionality. I suspect that this will be relatively simple, but what seems to be simple is often complicated.

Internal links
[1] 2023
[2] 2014



This day in blog history:

Blog #Date TitleTags
64508/11/2013Sorting in Excel via Delphi automationProgramming, Delphi, Excel, Office automation
90008/11/2015Conceptual changeDBA
90108/11/2015900 blogsMeta-blogging
143808/11/2021DBA updateDBA
155108/11/2022First rain of the yearWeather
185308/11/2024New CPAP maskCPAP

Thursday, November 06, 2025

New smart watch

Several years ago, I posted about a smart watch that I received for free from my health fund by virtue of walking a lot and accumulating points in their application. I had seen that my daughter has some form of smart watch (not from the health fund) and this planted a seed in my mind that maybe it was time again to try with a smart watch. Maybe they've got smarter over the past five years. 

The watch required 69,900 points (and an extra 60 NIS) for purchase (a site selling this watch in Israel has it at 423 NIS, discounted from 499 NIS); a week in which I achieve my walking goal gives me 1,500 points, and most days I earn an extra 30 points, so a normal week will see my point count increase by about 1,600 points. That means that I've been saving for about 44 weeks or ten months. At the moment my weekly goal is 'only' 58,500 steps a week, or 8,357 steps a day. There are days when I barely make this target and days when I far exceed it. As the purpose of the application is to encourage exercise, the weekly total is supposed to increase, but it's been about the same for the past few weeks. I could easily walk more, but if I do this, then the target will increase and then maybe I won't be able to make the weekly target. So I'm gaming the system at the moment by trying to make the daily target; normally once a week I will walk far more, so then on Saturdays I try to walk as little as possible in order not to increase the target. 

Once I received the watch, the usual trauma started: how do I charge it? How do I make it work? The charger cable comes with a USB plug at one end and a magnetic strip at the other that connects to the watch (but only one way around). After charging the watch for a few hours, I turned it on and tried to figure out how to use it. The instructions were mainly a set of pictures, too small to be useful, so they didn't help. I contacted my daughter, but she has a Samsung watch and so she could only give advice: the clock on the watch should synchronise once I run an application on my phone.

The booklet came with a QR code, but when I initially scanned it, an app for an online bank started installing. Reading the fine print a bit closer, it talked about installing 'Smart life', but this turned out to be an app that controls smart devices within the home. Eventually I scanned the QR code again and this time another app called 'Smart life' (or rather, 'Oplayer smart life') from Synergy Innovations started to install. It wasn't clear at first how it would connect to the watch: I could see the bluetooth icon on the watch but when I turned on the phone's bluteooth and started scanning, no watch could be found. Eventually the app connected to the watch and since then everything that appears on the watch also appears on the phone.

What does the watch do? Apart from telling the time (useful at night) and measuring steps, it also measures my pulse and oxygen saturation. Supposedly it also measures something to do with sleep, presumably light and deep sleep. It might also measure a few more things, such as swimming, but that can wait for several months.

How accurately does the watch measure steps and pulse? At the moment of writing, Samsung health says that I have walked 3,532 steps today, whereas the watch says 3,427. My pulse last night as measured by the sphygmomanometer was 80 bpm whereas the watch said 87 bpm. There was a similar gap this morning. Intriguingly, I can see a graph of readings starting at midnight although I can't see what each point represents. At midnight and until I got up at 5:30 am, the pulse was low; it then increased just before 6 am which is when I was walking the dog, then decreased again. 

The screenshot from the phone app gives an indication of the pulse ("heart health") without actually showing the values. Similarly with sleep: the app only started measuring at midnight (and yesterday there seems to be no sleep at all) so I 'slept' only 5 hrs 12 minuts (which is approximately correct if it's only from midnight); I think that light sleep was from 00:00 until 00:17 (17 minutes), then again from 01:35 until 02:53 (78 minutes), but after that there aren't enough times given that line up with the start or end of periods. To me, it looks about 50% each. 

I have just discovered that if I press on that graph (on the phone), I can get details for a day, a week or a month. So I spent 2 hours and 4 minutes in deep sleep and 3 hours 8 minutes in light sleep. I was in deep sleep for 39% of the time measured, which is considered normal, whereas I was in light sleep for 61% which apparently is high. That's slightly confusing: the deep sleep should be considered low if the light sleep is high. But where's my sleep from 9 pm the evening before? 

I can also see that my pulse decreased from 61 bpm to about 50 bpm during the night. Pressing on the pulse segment and requesting 'more data' gives me a reading for about once every half hour, including yesterday evening. Apparently my pulse was at 146 bpm yesterday evening at 20:26; as I was watching television at the time, one can only assume that my pulse increased because I was so digusted at the news. At 20:58 the pulse was back down to 68 bpm and decreased to 51 bpm at 22:34.

This is all very interesting, but not too valuable if the data don't match those measured in other ways (and at the moment, they don't!). On the other hand it can give indications and trends. I hope that the problem with measuring sleep will sort itself out.

Internal links
[1] 1419



This day in blog history:

Blog #Date TitleTags
5906/11/2006ShoppingVan der Graaf Generator, Peter Hammill, Musical instruments, Keyboard
21506/11/2009Eliza Carthy, "Red"Morse, Eliza Carthy
30106/11/2010Trains and mealsCooking, Trains, Slow cooker
108806/11/2017Forty years agoPersonal, Sandy Denny, Heron
118306/11/2018Intermediate thesis submitted!DBA
143606/11/2021Opening Word from a thread and displaying a fileProgramming, Delphi, Threads
155006/11/2022Appearances in my school's ChroniclesPersonal, Bristol Grammar School
185106/11/2024More dopamineProgramming, Blog manager program
185206/11/2024Guy Fawkes nightIsrael, Yuval No'ach Harari

Tuesday, November 04, 2025

Squamous Cell Carcinoma (SCC)

Today I had the operation for removal of a growth on my neck to which I referred1 six weeks ago. We arrived at the hospital far too early, and would have arrived even earlier had we taken advantage of a short cut that I wasn't sure about (the 'new' road 16 that surfaces very near the hospital). It was like waiting for an airplane: first we waited for the check-in counter to open, then I checked in, then after another wait, the plastic surgery clinic opened and a nurse checked me, then we waited some more then eventually another nurse called me into surgery.

The operation - if one can call it that - was very similar to all those that I've had before, although this time it seemed to be more involved in that a larger area of skin seemed to be removed. There was cauterisation (creating the smell of burnt flesh) and self-dissolving stitches, followed by the application of a relatively large bandage.

The tissue that was excised was sent to a laboratory within the hospital, and after a wait of nearly two hours, the result arrived: a squamous cell carcinoma had been removed. What's the difference between BCC and SCC?

  • Basal Cell Carcinoma (BCC) arises from basal cells located in the lower part of the epidermis.
  • Squamous Cell Carcinoma (SCC) develops from squamous cells found in the upper layers of the epidermis.

To you and me they're more or less the same. At the moment it's difficult to tilt my head because of the huge bandage, but tomorrow it will come off and life will go back to normal.

Internal links
[1] 2001



This day in blog history:

Blog #Date TitleTags
21404/11/2009HejiraRandy Newman, Joni Mitchell
42504/11/2011Coffee addicts (Millennium trilogy)Films, Meg Ryan, Steig Larsson, Time traveler's wife
42604/11/2011User defined menusProgramming, Delphi
143404/11/2021Sleeping deeplyCPAP, MP3, Binaural beats, Sleep
154804/11/2022Redesigning the 'blogs' programProgramming, Blog manager program

Saturday, November 01, 2025

More apneic events

I wrote1 ten days ago about the high number of apneic events that my CPAP machine had been recording. I had two hypotheses for these high numbers: the mask is reaching the end of its life, and the lack of amitryptiline. 

Well, today I reviewed the numbers for the past week, and whilst they're not quite as bad as those from early October, they're still far too high. I've been taking amitryptiline every night, so I can see that if it did have an effect, it's only minor. I'm going to order the new mask tomorrow, as we're now in November, and last year's mask was bought in November: I'm entitled to a free mask once a year. I don't think that the mask will have much of an effect either.

A third hypothesis occurs to me: maybe my weight loss is effecting the apnea. Since writing that blog, I've dropped another half kilo from my weight. I looked at my records and saw that I was nearly the same weight in May 2023: I had lost weight in a similar manner to that of today (although I soon started putting it back on), but there seems to have been no effect on the number of apneic events. As I suffer from central sleep apnea, which is caused by the brain failing to send proper signals to the muscles that control breathing, it seems unlikely that weight would have any effect.

I shall try to find a 'sleep doctor' whom I can consult.

Internal links
[1] 2024



This day in blog history:

Blog #Date TitleTags
77201/11/2014Undoing textual changesProgramming
154501/11/2022Big cloudWeather

Friday, October 31, 2025

Implementing CP Prolog - 6

Last week I presented the rule

time_in_minutes(TimeAtom, Minutes) :- atomic_list_concat(TimeAtom, ':', [HStr, MStr]), atom_number(HStr, H), atom_number(MStr, M), Minutes is H * 60 + M.

I ran out of time implementing atomic_list_concat (which is, along with atom_number, a standard Prolog predicate and not something that CoPilot invented); it wasn't as it appears above, but rather in the form atomic_list_concat ([H|T], ':', TimeAtom). I thought that I would start off this week by putting the arguments in the more logical order, but unfortunately this caused problems with the implementation of this predicate as it currently stood. So I had to work on this, finally getting the predicate to work both ways.

I thought it would be instructive to add logging to the goal resolver so that one could see what the current goal to be resolved is. This is helpful to see where the rule was getting stuck. I used some standard code of mine for doing this.

The standard predicate 'atom_number' should have been relatively simple to implement, but the initial code that CoPilot presented was insufficient. It hadn't considered the possibility of both arguments being variables, and in any case, CoPilot had neglected to resolve the arguments first before dealing with them. Eventually we got the code to work. In the goal resolver, first one must handle the current goal then if that succeeds, a recursive call must be made to resolve the remainder of the goals.

What does this mean? The initial goal is time_in_minutes (03:30, X). This gets replaced by the four clauses in the rule's body. If the goal atomic_list_concat succeeds, then a recursive call must be made with the three remaining goals, and if the next goal succeeds (atomic_number), then a recursive call must be made with the two remaining goals. This is something that I had missed in previous weeks for the comparison operators: I was assuming that these were the final goal to be solved, but in reality they could appear anywhere.

Once atom_number was working correctly, it was time to handle the arithmetic expession. Again, I had solved simple expressions such as 3 + 4, but here a recursive evaluation function was required. Fascinating code. I should point out here that depite all the sophistication of CoPilot, it made a rookie mistake here: one can't have a case statement where the value being evaluated is a string. This was easily corrected but just goes to show that the initial code is not always correct.

Finally the entire rule and goals were handled correctly, so that querying time_in_minutes (03:30, X) gave the solution X = 210. 

Before leaving this level of the interpreter, I wanted to refactor the common logic from ResolveGoals that I had been adding to the intrinsic functions that makes the recursive call to handle the remaining goals. This was partially successful: it broke down in places because the signature of AtomicListConcat, namely (args: PTermArray; var env: TEnvironment): boolean is not the same as that of AtomNumber, (atomTerm, numberTerm: PTerm; var env: TEnvironment):boolean. It was more successful with the comparison operators, although these too required helper functions.

What's next? First, I want to expand the 'flight finder' database - this requires the time_in_minutes rule to ensure that the passenger gets to the airport at least three hours before a flight is due to leave. But after that? I don't know. If I read some Prolog texts over the next week then maybe I will come across something that I would like to implement. But as I wrote a few weeks ago, I suspect that I am much more interested in learning how to write the interpreter than actually use it.



This day in blog history:

Blog #Date TitleTags
108631/10/2017Tom PaxtonPersonal
127031/10/2019Coincidence or deliberate reference?John Le Carre, Non-fiction books
154431/10/2022Extending cooking length for chickenCooking, Ninja grill

Thursday, October 30, 2025

Counting beats with Van der Graaf Generator (5) - After the flood

I've never particularly liked this song that closes the first "real" VdGG album, 'The least we can do is wave to each other'. I've always found it grandiose and striving too much for effect: two features that normally don't appear in other songs of theirs. Last night, the song came on my headphones whilst walking the dog, and this time I paid attention to the various time signatures being played. The results are more than interesting.

The opening riff is in 104 - the guitar strum is "on the 10". The next part, with the lyric "Clouds have gathered" has one bar in 44 followed by one in 64. I didn't realise this last night, but writing this now makes me realise that these lines could also be viewed as being in 104. The pace then slows for more bars in 44. The "chorus" ('And when the water falls again') is clearly in 44.

At the 4:50 minute mark, a humungous riff starts to be played that repeats several times: this is in 124 - I think it's correct to write it this way and not as three bars of 44 as every note is emphasized equally. If I remember correcly, each note is only played once during each sequence making this an example of "12 tone serial music". This carries on for another minute and a half (probably too much) before a simple passage with (initially) only acoustic guitar and bass: this is in standard 44, until the word 'Annililation' is cried out (as someone wrote, like a crazed Dalek) over the 124 rhythm that returns for a few elongated bars.

From hereon, we're back into 44 for the extended coda. The bass tends to play syncopated beats on the "and" of beat 4 of the previous bar, i.e. anticipating the first beat by a quaver.


This day in blog history:

Blog #Date TitleTags
42130/10/2011Sir Jimmy SavileObituary, RIP
42230/10/2011Succulent SundayCooking, Slow cooker
77130/10/2014Another good ideaERP
118130/10/2018Losing an old friend, gaining a new oneComputer
143330/10/2021Binaural beats and deep sleepCPAP, MP3, The brain, Binaural beats, Sleep
154330/10/2022Adding more old blog entriesProgramming, Meta-blogging, Blog manager program

Saturday, October 25, 2025

Implementing CP Prolog - 5

The goal of this week's installment is to parse, print and solve correctly a query such as
?- time_in_minutes (05:40, X), according to the rule shown below. If everything goes correctly, then X = 340 should be the result. 5 hours after midnight is 300 minutes, along with another 40 minutes. Here's the rule:
time_in_minutes(TimeAtom, Minutes) :- atomic_list_concat(TimeAtom, ':', [HStr, MStr]), atom_number(HStr, H), atom_number(MStr, M), Minutes is H * 60 + M.

The code from last week is line-orientated: it reads each line as a separate fact, rule or query, deleting the final full stop (in fact, frequently I would omit the full stop when typing as it didn't do anything). In order to read the above correctly, each line has to be concatenated in a buffer until a full stop is read at the end of a line (a full stop in the middle of a line should be ignored at this stage). The fix for this had to be implemented in two places: in the main loop that reads user input, and in the procedure that loads a text file.

Parsing the rule was more difficult. The head (i.e. time_in_minutes) was read correctly, but when I tried to query using this predicate, the first and only subgoal that appeared had the 'is' predicate. Obviously all the subgoals until the final one were being ignored. I tracked this down to a location where the presence of an inline operator in the line is checked before dividing the line into parts. This is a direct result of the queries that I was writing last week, such as add (Operand1, Operand2, Result):- Result is Operand1 + Operand2. I hadn't considered the possibility that the rule might have several clauses, where not all of them had inline operators. Once I found the problem, solving it was fairly simple: first divide the body (i.e. list of subclauses) into separate clauses, then handle each clause on its own. 

So the rule's body has four clauses, where the final clause is a 'triple whammy': three infix operators. This should be translated into the form: is (Minutes, * (H, + (60, M))). Now this complex term is built correctly.

The same problem of assuming that if the rule's body has an infix operator occurred in the printing (or 'showing') code. Again, each clause should be handled on its own, where the final clause gets passed to the recursive PrintInline function. Now the rule is printed correctly.

The rule assumes two intrinsic predicates, atomic_list_concat and atom_number. The latter is probably easier to understand; if the variable (first argument) is a number, return its value in the second argument. So if HStr has the atomic value '05' then H should be 5. The first predicate splits an atom whose value is <something> colon <something> (e.g. 05:40) into a list, where the first entry in the list will be 05 and the second 40. It's just as well that I did some work on lists, although I don't know whether it will be sufficient for this case.

For some unknown reason, I had modified the first clause in the rule and all my debugging was on this one clause.

?- atomic_list_concat ([H|M], ':', 04:30).

Maybe this is what I had originally and CoPilot changed it. Whatever. This query required all kinds of changes and I spent hours on it. Parsing and printing had to be changed to allow this new kind of list, [H|M]. Resolution also required changes. Despite the myriad changes, for quite some time the solution to the above query was H=H, M=M, which is a clear sign that the environment is not being updated somewhere. CoPilot says the environment should be passed as a VAR variable everywhere so I changed this wherever necessary. Now H= 04 and M = 30. Halleluya!

I don't have the necessary strength of will to implement the predicate 'atom_number' at the moment. I'm fairly sure that it's called something else in other implementations. At the moment, it won't actually do much - the term's name '30' will be replaced by '30' because name is a string field, but '04' should be replaced by '4'.



This day in blog history:

Blog #Date TitleTags
1925/10/2005The Time Traveler's WifeLiterature, Robert Silverberg, Time traveler's wife
21225/10/2009The importance of building indexesProgramming, SQL, Firebird
89625/10/2015The end of summer timePersonal
98425/10/2016What I did on my holidaysDBA, MIDI, Steig Larsson
143125/10/2021Blind date (new song)Song writing, Home recording
153925/10/2022Roland J-6 minisynth: a serious musical instrument or an expensive toy?Musical instruments, Roland J-6

Friday, October 24, 2025

Early deaths

Yesterday evening, my wife and I were watching the television news when an item was broadcast showing the eulogies at a funeral from one of the dead hostages whose body was return from Gaza. The funeral was accompanied by songs by a Moroccan singer, Amir Benayoun, whose voice is an acquired taste, and - how can I put this diplomatically - I haven't acquired the taste. I told my wife that I don't want him singing at my funeral, and she agreed, saying that she would rather try and get Nick Drake to sing.

I don't remember what my initial reaction to this was - amusement? disbelief? - but swiftly I told her that Nick died in 1974 at the age of 26. I then told her the edited highlights: how everyone involved with his first two records thought that they were wonderful, but that they never sold. Nick barely performed live (did I see him in 1970 with Fotheringay1? I still don't know) so he could hardly keep himself in the public eye. I told her how the disappointment at the commercial failure caused him to spiral into depression, and how he died. What it suicide or misadventure? No one will ever know. I told her of the popular renaissance that began in 1999 when Volkswagen used 'Pink Moon' in an advert (why that song? I have no idea) which caused his sales to suddenly come alive.

I should point out at this stage that my wife can identify the Nick Drake songs played on the radio, and the 'Unhalfbricking' version of 'Who knows where the time goes' is one of her favourite songs. She didn't like a few other songs that I sent her that are similar in style; I shall try with 'The sea'.

Following on from Nick, I told my wife of the last weeks of Sandy Denny: the drinking, Georgia and Trevor Lucas. At the end of the 1990s I was very involved in the Sandy Denny legacy, meeting and spending time with Sandy's close friend, Miranda Ward, and even copying discs for Georgia. I told her about Sandy's last concert tour that I was able to see and how a live album was made of the final concert, released in 1998. I told her how I had contributed some background material - a ticket, an album advert - for this project. 

As it happens, a few days earlier I had been ripping my Sandy discs to the computer, in the same way that I did with Richard Thompson's early work2. By chance I had looked at the booklet accompanying the album made of that final concert and was pleasantly reminded that my name appears in the sleeve notes.

So I dug out the disc again and showed the insert to my wife. She was suitably impressed. I also am thanked in a book about Sandy Denny.

But what song would I like played at my funeral? In a sense, I don't really care, and it also might well be that other people's perceptions of what might be the best song to be played won't match with mine. I think that I would like to be remembered by a song I wrote several years ago, 'Take these few words'.

Internal links
[1] 1313
[2] 1883



This day in blog history:

Blog #Date TitleTags
21124/10/2009Wizz Jones: The legendary me - and musings on music samplersNice enough to eat, Fairport Convention, 1971, Dave Evans, The village thing, Wizz Jones
29724/10/2010Copper socksHealth, Copper
76824/10/2014User resistanceERP, DBA
89524/10/2015Living in the past1970, Bristol Grammar School
184724/10/2024Midnight and BlueIan Rankin, Richard Thompson, Police procedurals

Tuesday, October 21, 2025

Problems with Whatsapp

Yesterday I intended to delete a message that I had sent to myself. By accident, I deleted the entire sequence of messages to and from myself. That's unfortunate, but not really a problem.

I then read some information on the net that explains how to restore data to Whatsapp in case one doesn't have an external backup, for example to Google - as I imagine many people don't have. The instructions were to find a certain file via the file manager on the phone and rename it; this file holds data regarding messages and is stored in one of Whatsapp's directories. This step was not a problem.

Then I was told to uninstall Whatsapp then reinstall it. When installing, one can ask for it to be restored from a backup which I supposedly now had. Except for the fact that the file that I renamed no longer existed ... because uninstalling Whatsapp deleted all its directories and all the files contained within. Reinstalling Whatsapp recreated the directories, but not the files. 😞

The instructions regarding the file that I renamed should have included moving it to another directory that is not one that belongs to Whatsapp.

So I start anew. It's far from the end of the world, but a trifle annoying. Of course, I also ensured that from now there is an automatic daily backup, although I suspect that this will fail at some stage because there is not enough free space in the Google directory. I shall have to try and find what I can delete from there in order to free up room.



This day in blog history:

Blog #Date TitleTags
5821/10/2006Busy morningProgramming, Randy Newman, Paul Simon
64221/10/2013Paul KleePersonal, Bristol Grammar School
108421/10/2017Sumptuous Saturday Seven - baked hake with potatoes and vegetablesCooking
168121/10/2023Seventh chordsMusic theory
184421/10/2024More blog manager tricksProgramming, Blog manager program

Monday, October 20, 2025

Sleep apnea and blood tests

The data from my CPAP machine for the past week or so has been very discouraging, showing several nights with 18-19 apneic events (most of them central), and no nights with the 0, 1 or 2 events that I normally have. What can be wrong?

My initial hypothesis was that there is something wrong with the mask, as it is reaching the end of its year. But there are no leaks being recorded so that's probably not the reason.

My second hypothesis is connected with evening medication. When we came back from Italy a month ago, I couldn't find the packets of two medicines in my special suitcase. I suspect that I hid them so well that I couldn't find them afterwards, and not that I forgot to pack them. So for the past month, I have been taking much smaller amounts of one drug that is the probable cause, amitriptyline, of which I take a small dose (25 mg) as a prophylactic against migraine. It is very effective at this, but it's also known to cause drowsiness. I posit that when I get a new prescription for this drug, the number of apneic events will swiftly decrease. Another drug that seemed to disappear is for blood pressure, although not taking it has not had any appreciable effect.

Another side effect of amitriptyline is weight gain. As it happens, since coming back from Italy, my weight has dropped by 2 kg! I now weigh 'only' 83.9 kg, which is the lowest for two and a half years. I have thought until now that this due to the fact that for the past few weeks I haven't been eating anything from after lunch until the next morning, thus restricting the hours during the day in which I eat to about 7 hours, with 17 hours fasting. Maybe the fact that I am not taking amitriptyline also has an effect on this; again, I'll know when I get the new prescription (next week). 

The biannual blood tests show improvements in almost everything:
Test27/03/2519/10/25Remarks
Triglycerides 192 178 Lowered - good
Total cholesterol 177 168 Lowered - good
HDL 40 48 Raised - good
LDL 99 84 Lowered - good
LDL/HDL ratio 2.48 1.75 Lowered - good
Uric acid (gout) 7.1 6.7 Lowered - good
Haemoglobin 16.3 15.7 Almost unchanged
NEUT/LYM ratio 2.1 1.8 Lowered - good
HEMOGLOBIN A1C % 6.0 6.1 Raised - not good
Glucose 103 107 Raised - not good
Potassium 5.3 5.1 Lowered - good

The lipid (cholesterol) profile has definitely improved, as has the potassium level (important for me), but the values for glucose and HEMOGLOBIN A1C % (glucose over the past three months) are worse and that is worrying. I had the value 107 for glucose a few years ago, after which it dropped slightly. Obviously I'll have to cut down on the biscuits and chocolate again, even if I'm not eating them after lunch. I'll try again with bell peppers1; I stopped because I thought that they were giving me stomach pains, but that pain may be due to something else.

Internal links
[1] 1720



This day in blog history:

Blog #Date TitleTags
20920/10/2009Lucky breakTV series, Films, Olivia Williams, William and Mary, Cold feet
41920/10/2011Firebird DB management tool (4) - CorrectionsProgramming, SQL, dbExpress
64120/10/2013More health issuesHealth, CPAP
76720/10/2014The seach for serendipityDBA, SQL
89420/10/2015Vinyl log 23 - 20 OctoberVinyl log, 1971, Dave Evans, The village thing
126820/10/2019This must be the place (2)Personal
135020/10/2020DBA updateDBA
168020/10/2023Sorting on two or more columnsProgramming, Delphi, ClientDataSet
184320/10/2024Good news for Smiley watchersJohn Le Carre

Sunday, October 19, 2025

Arithmetic in CoPilot Prolog

I decided to stop work on implementing lists in my Prolog interpreter, and instead I concentrated on implementing simple arithmetic. Bratko had a query in the form ? - X is 1 + 2, but I don't like that syntax. I had seen in another Prolog book that addition was defined by means of a rule that was more to my liking:

add (Operand1, Operand2, Result):- Result is Operand1 + Operand2.

So one could issue the query ?- add (2, 3, X). and hopefully receive the astonishing answer that X = 5. As usual, this is deceptive simplicity. As opposed to other rules that might have chained goals (such as grandparent (X, Y):- parent(X, Z), parent(Z, Y).), the tail of this rule has to be parsed in a different manner. First of all, the goals within the tail aren't separated by a comma, but even worse, the body is a recursive expression where the operators are infix. In a similar manner to lists, this should result in the following PTerm: is (Result, + (Operand1, Operand2)). As I wrote previously, 'is' is the assignment operator.

Figuring out how to display the rule was exceedingly painful, but eventually I figured it out.

function PrintInfix (body: pterm): string; begin result:= removeprefix (body^.args[0]^.name) + ' ' + body^.name + ' '; if body^.args[1]^.kind = tkCompound then result:= result + PrintInfix (body^.args[1]) else result:= result + removeprefix (body^.args[1]^.name); end;

The problem here was figuring out where the operators are and where the operands are. Once this was out of the way, I could start work on actually solving the goals. The only thing that was clear to me initially was that I would have to add another case to the ResolveGoals procedure in order to handle the possibility that goals[0]^.name = 'is'. I saw that in similar cases (e.g. where the operator is '<') that first I resolved the values of two terms. The first term resolved to the variable in the rule, i.e. Result, but the second term resolved to something more complex. After examining the data in memory, I discovered where the operator was and where the operands were. These data were passed to a new Calculate function that simply returns the result of the calculation (e.g. '2', '3', '+' should return 5 as an integer); I included the code for subtraction, multiplication, integer division and modulo - each operator required only one line. At the beginning of this function, the first two operands are checked to see that they actually contain integers; if somehow a non-integral value were passed then the function would return 'false' (this would require something stupid like ?- add (noam, 3, X) which doesn't make any sense anyway).

Calculate did return the correct value, but then I was stuck: how did I return that value to the calling procedure for display? Here I invoked my co-author CoPilot again, and although he messed up a few times, I could see what the code needed to be.

So now I can run queries such as ?- divide (11, 2, X).   X = 5; and ?- modulo (11, 2, X). X = 1.

As it happens, I had no need whatsoever of the recursive infix expression parser as there weren't any expressions such as (2 + 3) * (4 + 5) to be evaluated. I can't really imagine a query that would require such an expression so I'm not going to waste any brain time on this.

Although ... yesterday1 I wrote about checking the time that one has to be at an airport prior to a flight and stated an expression  (T1 - Min) > 3. This does have need of an infix expression parser, although by the time the expression gets stored, it's not longer in infix form. It might be that because of the fact that the function Calculate should return an integer for T1 - Min, I could extend the 'greater than' operator to convert the second operand also to an integer in order to check their values. I'm not sure that '25' would be considered to be greater than '3', because '2' is generally less than '3'. 

I didn't mention yesterday that connections within an airport also take time that the naive database ignores. On the other hand, a database about trains could ignore those problems ... except for Milano Centrale, where the platform for the train to and from Malpensa is at one side, and of course the platform for Rapallo is at the other side of the station. It took at least 10 minutes (if not more) to get from one side of the station to the other, and when we returned from Rapallo, I also had to buy a ticket for Malpensa, causing even more of a delay. There would be no problems for Israeli trains, especially as I don't have to buy tickets anymore.

Internal links
[1] 2022



This day in blog history:

Blog #Date TitleTags
76619/10/2014Literature review: first draft completedDBA
126719/10/2019Juliet (naked) - the filmFilms, Nick Hornby
167919/10/2023Think again: the power of knowing what you don't knowHealth, Non-fiction books
184219/10/2024This year's headphonesHeadphones, Temu

Saturday, October 18, 2025

Updates in CoPilot Prolog

A week ago, I thought that I would look for material discussing the first ever implementation of Prolog. It turns out that the Fortran implementation1 was not the first, as per CoPilot. I found a paper written by Alain Colmerauer and Philippe Roussel - actually two versions of the same paper - that states During the fall of 1972, the first Prolog system was implemented by Philippe in Niklaus Wirt's [sic] language Algol-W ... From June to the end of the year [1973], Gérard Battani, Henry Meloni, and René Bazzoli, postgraduate students at the time, wrote the interpreter in FORTRAN and its supervisor in Prolog.

At the end of the paper, a very simple database was shown that contains data regarding flights from Marseille to Paris and thence to London (there should have been also flights from London to Edinburgh, so that one could travel from from Marseille to Edinburgh, the two main locations for Prolog development). I thought that it would be interesting to see how CP Prolog would handle this so I created a small text file with a few of the facts and fed it in. To my surprise, no syntax errors were detected and one could actually query the facts successfully.

flight (paris, london, 00:30, 03:00). flight (paris, london, 09:30, 12:00). flight (marseille, paris, 06:00, 07:00). route (A, B, Min, T1, T2):- flight (A, B, T1, T2), Min < T1. route (A, B, Min, T1, T3):- flight (A, X, T1, T2), Min < T1, route(X, B, T2, T2a, T3), T2 < T2a. % sample query: % ?- route (marseille, london, 03:00, Departure, Arrival). % Departure = 06:00 % Arrival = 12:00

Much later I thought of a few problems with this very simple database. The first is trivial: CP Prolog is unaware of a 24 hour clock (or indeed of any clock), so a flight that has a departure time of 22:00 and an arrival time of 00:30 would appear to arrive before it departs! 

The second problem is much deeper: the database does not take into account the fact that one should be in the airport three hours prior to an international flight, and that it takes a finite amount of time to get from wherever (home, university) to the airport. This implies that there should be a clause in the 'route' rule along the lines of (T1 - Min) > 3. Unfortunately CP Prolog would have three problems with this: it doesn't know how to perform arithmetic, the syntax is problematic, and 3 has no meaning here. Min might be 03:00 and T1 06:00, but we are mentally converting these string values into interger values before performing the subtraction. So it seems that a conversion rule such as ClockToMins would be necessary, although I don't know how to write this in Prolog (easy in Pascal). Then the two converted values would have to be compared, where the difference would have to be greater to equal to 180 (that I can do).

I could implement arithmetic without too much trouble, as the first thing that I wrote with CoPilot was a simple infix expression parser2. Prolog syntax would have expressions such as X is Y + Z, where 'is' is the infix operator for assignment. I have seen books that replace this operator with ':=', which is the Pascal assignment operator; I could support both with no problem. As Bratko ("Prolog programming for artificial intelligence", 1986), writes (p. 85),

The following question is a naive attempt to request arithmetic computation:
?- X = 1 + 2.
Prolog will 'quietly' answer
X = 1 + 2

and not X = 3 as we might possibly expect. The reason is simple: the expression 1 + 2 merely denotes a Prolog term where + is the functor and 1 and 2 are its arguments. There is nothing in the above goal to force Prolog to actually activate the addition operation. A special predefined operator, is, is provided to circumvent this problem. The is operator will force evaluation. So the right wav to invoke arithmetic is: ? - X is 1 + 2.
Now the answer will be:
X = 3

Do I really want to add this? I suppose the answer to this rhetorical question is 'why not?'. I estimate that 90% of the code required already exists in one form or another. Adding the infix expression parser would be a good excuse for dividing the monolithic Pascal source file into several units; apart from speeding compilation, this would also help to separate logical parts of the program.

The only reason that I hesitate is that yesterday I started work on implementing lists. These are very much the legacy of Lisp in Prolog, and once again they are deceptively simple but actually difficult to implement. A list is a sequence/collection/set of items - Bratko's initial example has ann, tennis, tom and skiing, whereas my simple test used the items 1 and 2. They are written like this [1, 2] and one would expect the 'show' command in CP Prolog to display the list in this form. The truth is more complicated.

In Lisp, a list can either be empty, in which case it is represented by the value nil, or it consists of a 'cons' cell, in Pascal a record, that has two fields: a head and a tail. The head 'can be anything' - in CP Prolog, this means a PTerm, whereas the tail is usually a list - this also means a PTerm. The very simple list [1] is internally represented as . (1, nil): here the functor/predicate is '.' (pronounced 'dot', or if one is a Lisper, 'cons') that has two arguments, where the first is the atom 1 and the second is the atom 'nil'. As such this is identical to the more normal Prolog fact parent (noam, netta). But if the list [1, 2] is presented to Prolog, the following structure will be built: .(1, .(2, nil)). This is not very easy to parse in CP Prolog and also difficult to display. The following code is deceptively simple (a phrase that I seem to use frequently when writing about Prolog) but that is only because each function is recursive.

function ParseList (line: string): PTerm; var i: integer; head, tail: pterm; args: array of PTerm; begin i:= length (line); if line[1] = '[' then line:= copy (line, 2, i - 1); dec (i); if Line[i] = ']' then Delete (Line, i, 1); i:= pos (',', line); if i = 0 then begin // list with one member SetLength (args, 2); args[0]:= atom (copy (line, 1, length (line))); args[1]:= atom ('nil'); result:= compound ('.', args); end else begin head:= atom (copy (line, 1, i - 1)); tail:= parselist ('[' + copy (line, i + 1, length (line)) + ']'); result:= compound ('.', [head, tail]); end; end; Function PrintList (head: pterm): string; var i: integer; args: ptermarray; begin result:= ''; args:= head^.args; for i:= 0 to length (args) - 1 do begin if (args[i]^.kind = tkAtom) and not args[i]^.nilflag then result:= result + args[i]^.name + ',' else result:= result + printlist (args[i]); end; end;

It took quite some time to achieve this code. About an hour after completing this, when I was in 'contemplation mode', I had several unpleasant insights. In parsing a line, I had originally said to myself that if the current line contains the [ character then the line should be handled by the ParseList function; this test occurs before the test for a rule (does the line contain the ':-' digram?), as a rule won't contain a list. Unfortunately that assertation is completely untrue. Both the left hand side of a rule and the right hand side of a rule can contain lists, otherwise all the predicates such as 'member', 'cons' and 'append' could not be defined (Bratko uses slightly different names for these predicates). So whilst ParseList as it stands is almost correct, I will have to rework ParseRawLine - this shouldn't be too difficult.

A minor correction but with much greater importance in ParseList is the replacement of the line
args[0]:= atom (copy (line, 1, length (line)))
with
args[0]:= ParseRawTerm (copy (line, 1, length (line))).

In my tests, I had written [1, 2] but I could have written [X, Y] in which case the call to 'atom' is absolutely wrong. There's also no need for the internal begin/end pair in PrintList but that's a stylistic issue without sytactic meaning.

The next stage will be to update unification so as to allow pattern matching like:
member (X, [X | _]).
member (X, [_ | T]):- member(X, T).

Here we have a list in a rule as I mentioned earlier. The '_' character is the anonymous variable, but here it's simply standing for 'nil'. The first rule means that X is a member of the list [X, nil] (or if one prefers, X is a member of the term . (X, nil)), whereas the second means that X is a member of the list where X is somewhere in the tail. At the moment I have no clue as to how to go about this.

Internal links
[1] 2013
[2] 2010



This day in blog history:

Blog #
Date
TitleTags
29618/10/2010
Mocha too goes on holidayDog
41818/10/2011
Dennis Ritchie: The man who created UnixObituary, RIP
117918/10/2018
Conditional choose-field trigger (2)Priority tips
184118/10/2024
Completing the auxiliary programProgramming, Delphi

Friday, October 17, 2025

YouTuber discovers Nick Drake

YouTuber Charles Cornell has discovered Nick Drake (better late than never) and has devoted a few videos to his songs. The first (that I know of) is about 'Riverman', although I would never have known this from the title of his video, 'The most hauntingly beautiful 4 chord song ever written'. Those four chords are Cm add 9, Eb7, Ab and C add 9. Whilst the chords are definitely something special, the rhythm (often written as 5/4 but it seems more like 10/4) is also special, as is the string arrangement that grows and grows throughout the song.

As it happens, I heard 'Riverman' on the radio yesterday. Someone on the station has a thing for Nick Drake: at one stage, we used to hear 'Northern sky' fairly regularly, but lately they've begun mixing this up, and I've heard several of his songs played*. A few days ago I heard 'One of these things first', and as it happens, all of Nick Drake's songs were playing on my headphones in the past few days, so 'One of these things first' resonated.

Today we have a new video about 'One of these things first', although again, one would never know it from the title, 'The ONE chord that turned a simple progression in genius'. There's no need for such hyperbole. I had never paid attention before, but this song is in 3/4. The introduction has a bar of E, a bar of A, a bar of B; the fourth bar is more interesting as it has a Bb chord played as a dotted crotchet and an A as another dotted crotchet. Apart from the Bb - which is a very interesting chord in itself, but obviously is a passing chord between the B and the A - this bar is effectively played in 6/8, or as one might say, 2 against 3. A polyrhythm. 

But the ONE chord to which the title refers is the G chord in the verse: again, the chord sequence seems simple, but sophisticated. Cornell was very enthusiastic about this, especially when considering that later on in the verse where one might have expected the G to appear (that is, 'expected' after one has got over the unexpected first appearance), it doesn't.

To me, at least, Cornell rambles a bit: the knowledge content of his videos could be compressed, or in other words, David Bennett imparts much more knowledge in one of his videos. After all, that G chord is simply a chromatic mediant - words that Cornell does not say. Cornell does not consider the fact that Drake's songs were mainly written on guitar, often with unusual tunings, so the whole idea of 'chords in a key' has less meaning than on a piano. Sliding down by one fret is something that guitarists don't think twice about, but it's a foreign idea to the piano.

It would have been more instructive to talk about the unusual and irregular vocal phrasing - bars with no vocals, bars where the vocal starts on beat 3 and similar. That's along with the polyrhythm in the introduction.

* On the other hand, whilst 'Who knows where the time goes?' by Fairport Convention is played about once a week, they have yet to play anything else (that I have heard) by Fairport.



This day in blog history:

Blog #Date TitleTags
29417/10/2010Sweet and sour chickenCooking, Slow cooker
29517/10/2010Project Management courseMBA, Project management
41717/10/2011Getting ready for the KindleKindle, E-book
98317/10/2016Who's watching who?Grandfather
134917/10/2020Completing the story of porting an application to Windows 10/Delphi 10.2Delphi, Unicode
184017/10/2024Extending the auxiliary programProgramming, Delphi