Thursday, December 24, 2020

CLSQL in LispWorks: Varchar Fields

 I was attempting to use CLSQL in LispWorks and had trouble reading in strings. I could even output the strings using insert, but couldn't read them in properly. I had two main issues that I needed to make changes for (running LispWorks 7.1.2 64-bit running on Windows 10, update, now running version 8.0.1 and this is working).

First, LispWorks didn't like a return type of :unsigned-long-long in "...\quicklisp\software\clsql-20160208-git\uffi\clsql-uffi.lisp". Replace these references to :unsigned-long-long with '(:unsigned :long :long) and then it will compile properly. I can't speak for whether this 100% right and I don't think that any of my current code depends on getting this right. But, it does let me compile.

The next problem was in "...\quicklisp\software\uffi-20180228-git\src\strings.lisp", the function fast-native-to-string. There's two such definitions which are split across different versions of lisp. LispWorks 7.1.2 is in the first such definition and there is a reference to base-char that is specifically used for LispWorks.

Edit: Based on comment below from a reader, the changes to make here are:

  • line 500: #+(or (and allegro (not ics)) lispworks4)
  • line 513: #+(or (and allegro ics) (and lispworks (not lispworks4)))

You will end up with:


I made a simple table with data that contains both varchar(50) and nvarchar(50) to test with, defined below. I put (mostly) the same data in each of Name and Name1 to see if there was any difference.


Using clsql to get all of the tuples that were entered is as simple as (clsql:query "select Id, Name, Name1 from TableWithStrings").

I'm not able to find who to contact regarding this as I see that the website for the project may have been abandoned (last change on the change list was in Jan 2016, specifically for LispWorks support). I note however, that someone more recently made a change for uffi as the folder name has the date in it suggesting Feb 2018.

Saturday, May 30, 2020

BMI Table with Undue Precision

The undue precision will be on my part because this post isn't really about your health, it's about doing a little mathematics. In a spreadsheet, because sometimes it's more fun there.

I have a little spreadsheet with a few examples of interesting formulas in Excel. The thing that is a bit pitiful about it is that it isn't an inline formula but a dependency which makes it hard to copy and paste. I did a piece-meal copy and paste job from here (NIH) into a table. 

If you just want a spreadsheet that does this stuff for you and don't care about my blathering, download it here.

The thing that makes this table a beast is that the general usage requires you to first choose only one row of the table based on your height and then find which two weights you fall between and look up to find the BMIs you are between. And since this is a post about interpolation, you do straight interpolation to get your BMI. So, if I'm 5' - 10 1/4", and I'm dead sure to rights I'm not a 1/4" shorter, how do I proceed to get a more accurate value.

Of course, it is eye-roll worthy to care about such precision when the BMI number is a ballpark notion, anyway. But, from the stand point of data and formulas, can we do it anyway? (And, what might we learn if we do?) Here is what I have right now in all of its glorious ridiculousness:


Fig. 1 - Interpolation on steroids. Or maybe, interpolation without boundaries. Well, ok, interpolation without reason.

The simplest way of combining linear interpolations I could think of was to determine boundaries and look up the corresponding values. So we start with finding the low and high lines, in this case the lines for 70 and 71. Looking up the BMI weight included array formulas on the right hand side of the table that reference the given weight.

On the far right of the table I have formulas like the following in columns AL to AO:
  • =MAX(IF(B4:AK4<=B$25,B4:AK4, 0))
    • find the largest value in B4:AK4 that is still smaller than the weight in B25
  • =MIN(IF(B4:AK4>=B$25,B4:AK4, 100000))
    • find the smallest value in B4:AK4 that is still bigger than the weight in B25
  • =LOOKUP(AL4, $B4:$AK4, $B$2:$AK$2)
  • =LOOKUP(AM4, $B4:$AK4, $B$2:$AK$2)
It is important to use the <ctrl> + <shift> + <enter> key combination to make the first two of these formulas work as this is how you create array formulas out of formulas that are not inherently array based. (Formulas that are expecting ranges by default are inherently array based.) Taking the first formula as an example, the meaning of the syntax is something like this: "For every value in the list  B4:AK4, apply this formula." The formula being applied is the IF.  Anywhere you see B4:AK4 you can read, "any x that is in B4:AK4." If you C#'d this, you have something like:

     (B4:AK4).select(x => if(x < B25, x, 0)).max()

LOOKUP is listed as available for backward compatibility. Sounds like it is not really desired and probably the VLookup and HLookup functions are mostly preferred, but LOOKUP has a leg up on them in one respect. I don't have to modify the layout of my table to accommodate my present purpose. I need the search value to be the current row and the return value to be the top value of a column. If I use HLookup, I need the searched value to be the top row (as far as I can see, at least). If I didn't have LOOKUP, I still have a way, of course. Copy the BMI to the bottom and return the bottom row. To my mind, the design of LOOKUP communicates purpose much more effectively: "Here is what I'm searching--shown first, Here is what I want returned from, respectively to the matching item in the search space." 

The rest is just repeated application of linear interpolation. Do linear interpolation for 70 inches (in my case) and then for 71 inches. Then interpolate between those resulting BMIs to get a final BMI. In some cases I omit denominators and/or calculations where I know the result will give me 1 due to the way the spreadsheet and data set is constructed and so these get simplified out of the formulas.

Saturday, February 8, 2020

Cluster / Hierarchical Hashing in Common Lisp

I had some application ideas where I wanted to group data by several keys. Sometimes I care about the hierarchical relationship and sometimes I don't. As an example, let's take my FitNotes exercise data. Here's a few records from way back in the day (I can hardly believe the numbers, ugh...a few years and many pounds ago):

Date Exercise Category Weight (lbs) Reps
2015-03-10 Deadlift Back 130 10
2015-03-10 Deadlift Back 130 10
2015-03-10 Deadlift Back 130 10
2015-03-10 Chin Up Back 205 4
2015-03-10 One-Arm Standing Dumbbell Press Shoulders 25 12
2015-03-10 One-Arm Standing Dumbbell Press Shoulders 25 12
2015-03-10 One-Arm Standing Dumbbell Press Shoulders 25 12
2015-03-10 Flat Dumbbell Fly Chest 30 10
2015-03-10 Flat Dumbbell Fly Chest 30 10
2015-03-14 Deadlift Back 135 10

If I wanted to show a history of data and provide the user with the option of seeing a graph of their progress (similar to what the FitNotes application does) or perhaps a list of dates to select from to see the details on that date, then I would group first by Exercise and then by date. Depending on the purpose of the grouping, I might want to group by Category first. But let's go with a graphing application for a particular exercise. I want to group by Exercise and then by date.

I create hash tables at each level except the bottom level where I create a queue. The main thing that interested me here was the setf function, since the syntax bamboozled me the first time I saw in Practical Common Lisp (it's not hard, just different). I ended up not needing it for my current application, but may want it another day. For the kind of data I'm working with you need to use the equal test in order to get your hash tables to work with strings. If you compare (setf get-cluster-hash) with add-to-cluster-hash, you'll notice they are very similar. I was tempted to make add-to-cluster-hash out of the setf and get-cluster-hash functions but realized that that entails evaluating the hashes twice for adding the first record to a cluster-key. There might be a way to eliminate the code duplication, but I gave in to copy-paste-modify. (Please don't hate me.)



Here is a simple demonstration REPL session with some of these functions:



Since I have the data of interest in a CSV file exported from FitNotes, I can just read every line of the CSV file and use add-to-cluster-hash for each line, thus grouping my data without loosing the order of the items for each (exercise, date), and all without thinking much about the details of the data structure used to do the grouping.

Friday, January 3, 2020

Macro Expansion Time: Connection Strings

Connection strings bother me. I'm not saying that aren't a good solution or that they shouldn't be, but I don't like having to look up special values to fill in and so on. I think the reason connection strings exist is that the available options for drivers, servers, and several other settings is unknown. There is no definitive list of all the potential types of things that need to go into a connection string. There are, however, common patterns of connection strings and values that are normally required. There's a website devoted to connection strings (https://www.connectionstrings.com/) and as far as being a very generic resource for helping you with your connection string needs, this goes a long way.

But it would nice to be able to maintain a list of connection string values that apply to various connections or connection types that matter to me so that after I have learned the values I need for certain types of connections I can represent them with a keyword and if I use a bad keyword (e.g., typo), get a warning at compile time of that fact. In the process of starting a very basic macro framework for doing this, I ran into a bump in my understanding of the macro expansion process and I thought that made it worth sharing more so than the connection string macros themselves. I'm learning to use the clsql package (available with quicklisp), which comes into play with one of the macros.

Two notable features that these macros will demonstrate are:
  1. the explicit expansion of macros within code that produces code
  2. conditional code production based on the type of data supplied
    1. In particular, variables versus literals versus keywords
The first thing I want is to have my own repository of special values for each type of information that I care about. For my present interests I am concerned with drivers and servers. You might add to this specific databases. We don't want to get stuck with having to use keywords only because this would require having every case we will ever care about in the repository or require the repository to be updated before you can call the macro, which largely defeats the intended convenience of the macros. We also don't want the look up to happen during run-time where we can avoid it so that our convenience function doesn't add operations to run-time that would have been unnecessary if we had used a less convenient literal connection string. (Sometimes we have to decide between clarity/convenience of expression on the one hand and efficiency on the other. In this case, we will try to have our cake and eat it too.)

The way we handle our repositories is with a plist and the macros for each category of thing could be similar. I show only the drivers one since the server version is not materially different. (That probably means I could take the abstraction another level to remove the repetition.)



If a literal keyword is supplied, we will get the magic string that it corresponds to. We assume that if the user has supplied a keyword that isn't in the list, that they have made a mistake and therefore issue an error. This error is raised during macro expansion time which happens prior to compilation. If it is not a keyword, then we don't try to determine anything about its validity—we let the caller take their chances. It might be a literal string or it might be a variable, but that isn't this macro's problem.



Our next macro has some more interesting features, although it is basically a glorified concatenation. One notable feature is that we will pass on requiring keyword arguments and simply use &rest and recognize what we can and let everything else pass through without a lot of scrutiny. This is a choice to avoid doing research to support cases for my abstraction that I may never use. As the maintainer of this code for myself, I can always update this macro to support additional cases in the cond statement when I come across a new requirement that I actually want to use. In general, we are expecting keywords followed by either values or keywords that apply to the keywords, but we can accept strings in place of any of the keywords. We take consecutive pairs of parameters and consider the first of a pair to be the left hand of the = and second of the pair to be on the right of the = in our connection string.



Here's a sample usage:



(Note that you can use the function write-line to convince yourself about the rectitude of the escaped backslash.)

First note the explicit calls to macroexpand with the macro. The key to understanding why this is necessary it to think about the order of execution. Suppose instead of (list "Driver" (macroexpand `(typical-drivers ,b))) we wrote simply (list "Driver" (typical-drivers b)). When the macro is expanded, it expands the macro typical-drivers without having a specific value for b supplied and as such, it is an unbound variable called b which gets passed into typical-drivers. So, the code (typical-drivers b) gets expanded to be just b. Not very useful, is it? The macroexpand wrapper allows us to delay the execution of the expansion until b is bound to a value and the , notation puts the bound value of b in here and we get the desired result.

As we go through the list of pairs, we restructure them into an association list of (left side, right side) cons cells. We want to distinguish between two kinds of pairs. Bound and non-bound. The reason this distinction matters to us is that the bound items already have values and we can put them in place right away so that we don't have to wait until later to do the final concatenation for this part of the code. If there is no unbound part, then we will have a simple, complete connection string that will in our compiled code. If there is an unbound part, then we need to return code that concatenates the pieces. It is the responsibility of the caller to provide a way for the supplied variables to be bound to a value before entering into the code that is thus generated. A wrinkle in my terminology is that bound is something that applies to symbols. If I pass in something that is not a symbol (which I will do often), then I am going to assume, for simplicity, that it is a concrete and directly usable value and hence "bound" in the sense of this macro. 

Finally, my last macro is the usage of this connection string stuff to connect to a database using clsql, where my own original exercise began:

Tuesday, August 6, 2019

Using Calculated 1 Rep Maxes as an Index for Progression

I generally use an estimated 1 rep max as the measure of my progress in training for any particular exercise that I do. The FitNotes app somewhat encourages this thought process by providing a graph view that plots estimated 1 rep maxes over time. I don't disagree with the idea in principle. In fact, I had an idea that I might want to make a point of using 1 rep maxes as a method planning my progressive overload.

Progressive overload is the catch phrase that refers to adding additional training stimulus over time. From a given training stimulus, you can increase it a few ways, some of which lend themselves to different goals:
  1. Increase the weight
  2. Increase the repetitions per set
  3. Increase the number of sets
  4. Decrease the rest period
Doing any of these, or a combination of these, can increase the training stimulus so that you encourage additional growth. There's plenty to read out there about the difference between training for strength and training for muscle growth, but there may be a case to be made for balancing both of these goals. 

In very rough, non-technical terms: 
  1. Strength means your central nervous system is better at firing off more muscle fibers--maybe more intensely. Training for strength in this sense is normally done using lower repetitions per set.
  2. Muscle growth means the muscle fibers are bigger. They are usually able to move heavier weight than they used to as a result of being larger. Training for muscle growth is normally done using higher repetitions per set.
These are different strategies for training your muscles, and the divergence between them may or may not admit to synergies in their combination. Or, maybe it just doesn't suck too bad to mix it up a bit. I don't know. I'm not sure anybody really does. But I thought it would be interesting to use calculated 1 rep maxes as an index for the purpose of choosing a combination of weight and rep changes that results in a kind of theoretical progressive overload. I don't imagine I'm doing anything new, but hopefully, the spreadsheet I cooked up can make it easy enough for you to apply in your own training, if you don't think it's crazy.

Estimated 1 rep maxes are based on charts like this one: https://strengthlevel.com/one-rep-max-calculator. The idea is, if I do shoulder press with 95 lbs and I can do 12 reps before failure, I want to know how much weight I could do shoulder press with 1 time. According the chart I linked to, if I can do 12 reps with a given weight, that weight is 71% of my 1 rep max. So, if I divide 95 by 0.71, I am looking at my calculated 1 rep max. So, as long as you have a good chart of percentages, your calculation is pretty easy and is easily done in a spreadsheet using a look up or index function. I nabbed the percentages from my FitNotes app and am using them in my spreadsheet.

Reps % of 1 Rep Max
1 100
2 97
3 94.5
4 91.5
5 89
6 86
7 83.5
8 80.5
9 78
10 75
11 73
12 71.5
13 69.5
14 68
15 66.5

Using this chart on a sheet named '1 rep max percentages', I use the index function to drive a calculation. Along the left hand side I have the weight that is lifted and along the top I have the number of repetitions. To interpret the chart, look on the left hand side for the weight you lifted and find the column that corresponds to the number of repetitions you did. The number at that intersection is your estimated 1 rep max--provided the percentages driving the spreadsheet are legit. If you come across a chart you think is better, you can edit the values in that table and that will be reflected in the estimated 1 rep max.

Weight 1 2 3 4 5 6 7 8 9 10
2.5 2.50 2.58 2.65 2.73 2.81 2.91 2.99 3.11 3.21 3.33
5 5.00 5.15 5.29 5.46 5.62 5.81 5.99 6.21 6.41 6.67
7.5 7.50 7.73 7.94 8.20 8.43 8.72 8.98 9.32 9.62 10.00
10 10.00 10.31 10.58 10.93 11.24 11.63 11.98 12.42 12.82 13.33
12.5 12.50 12.89 13.23 13.66 14.04 14.53 14.97 15.53 16.03 16.67
15 15.00 15.46 15.87 16.39 16.85 17.44 17.96 18.63 19.23 20.00
17.5 17.50 18.04 18.52 19.13 19.66 20.35 20.96 21.74 22.44 23.33
20 20.00 20.62 21.16 21.86 22.47 23.26 23.95 24.84 25.64 26.67
22.5 22.50 23.20 23.81 24.59 25.28 26.16 26.95 27.95 28.85 30.00
25 25.00 25.77 26.46 27.32 28.09 29.07 29.94 31.06 32.05 33.33
27.5 27.50 28.35 29.10 30.05 30.90 31.98 32.93 34.16 35.26 36.67
30 30.00 30.93 31.75 32.79 33.71 34.88 35.93 37.27 38.46 40.00
35 35.00 36.08 37.04 38.25 39.33 40.70 41.92 43.48 44.87 46.67
40 40.00 41.24 42.33 43.72 44.94 46.51 47.90 49.69 51.28 53.33
45 45.00 46.39 47.62 49.18 50.56 52.33 53.89 55.90 57.69 60.00
50 50.00 51.55 52.91 54.64 56.18 58.14 59.88 62.11 64.10 66.67


The contents of cell B2 are =$A2 / (INDEX('1 rep max percentages'!$A$2:$B$16, B$1, 2)/100).  The index function takes three parameters:
  • Look up table: '1 rep max percentages'!$A$2:$B$16
  • Index (row) of table: B$1
  • Column of table: 2
The $ signs are a way of saying to Excel, "when you copy me to another cell, don't change me, keep me the same." So, the parts of the cell references that don't have a $ sign in front of them change when you copy them (relative copy), but the $ sign parts do not (absolute copy). The table of percentages is fixed with the $ signs and the row number (B$1) is fixed so that when you copy the cell down, it still looks to the first row for the number or reps. 2 is the column of the percentage table we need to feed into the formula. The table is pretty easy to make.

Here are links to the spreadsheet in Excel format and a print out to PDF, if you can't be bothered making the spreadsheet yourself.
Progressive Overload
Using the chart to help you with progressive overload is probably pretty obvious, but it maybe deserves some brief comments. Suppose you are doing an exercise with 155 lbs with 3 sets of 12 repetitions. 12 reps is not actually the most you can do in one set, but we don't care about your real 1 rep max. It's just an index. The fact that the index is used sometimes for calculating 1 rep maxes doesn't matter to us. It is just a way of guessing at a progression. We don't care that it isn't a "real 1 rep max", we only care that by interpreting this calculation as an index, we can use it to provide guidance for switching up our weight and reps at the same time.
It's just a number.
But it's a number that can help you.

Figure 1 shows some highlights of a way forward from 3 sets of 12 reps with 155 lbs to gradually move toward lower reps and higher weight while (hopefully) targeting an effectively greater stimulus. After you manage a full 3 sets of 12 reps, on your next workout after that, try for 3 sets of 10 with 165 lbs. In theory, if you achieve that, you have improved your strength (in some sense). If you don't succeed, you try again next work out, until you do succeed. Your next target could be 3 sets of 9 with 175 lbs. 

Fig 1. Highlights show a possible path of progression.
To the extent that this method of indexing the effective training stimulus may be valid, it can be used in a variety of ways. If you have been lifting very heavy, low reps, and you want to provide your tendons with a break or just change things up and lift light for fun, you can use this chart to help you decide what your target should be.

It's important to note though, that if you significantly change the number of reps per set, it will have a potentially large impact on your accumulated fatigue from set to set. My own experience suggests to me that with high repetition work, I accumulate a high amount of fatigue and with lower repetition work there is less of a fatigue angle involved. This confounds the matter when you make a large change to the number of reps and it probably comes apart at the seams a bit. These numbers may still help you get in the ball park.

Friday, May 24, 2019

Recursion and Accumulators

Working through Paradigms in Artificial Intelligence Programming, I have worked to wrap my head around accumulators and I have implemented one that worked surprisingly quickly. My example is a factorial design experiment. I have 4 reference implementations of a factorial design on an experiment with 7 factors, with each factor having 10 treatments in it. This is a bit of a ridiculous size, but it gave me clear separation of performances. I have split the code up into sections so you can focus on the parts of the program that matter you.

Supporting Code



Main Functions



Speed Testing

To make the testing as fair as possible, I started emacs fresh for each test run. Under these conditions, factorialize took 2.5 seconds, factorialize-2 took 3.7 seconds, factorialize-3 took 22 seconds, and factorialize-4 took 12 seconds.

A large amount of the time spent in these examples is spend in garbage collection. The factorialize-4 example could probably be improved by allocating a large chunk of memory in a 2-dimensional array and then the rest of the work involves a lot less memory allocation time. The land slide difference that marks factorialize off from the other methods appears to be the amount of shared memory it involves. Some reassignments of various locations of the resulting list, produce changes in other lists since there is shared cons cells among the lists. For my particular application this was proving to be a problem and so I have changed over to the factorialize-4 version of the function. Since my actual likely data is much smaller than that used in the speed tests of this post, I'm not overly concerned about the difference in speed.

An advantage of the method of factorialize-4, which uses an enumerator is that this method could be used to output data to a database or file in a way that didn't need to use very much run-time memory. Very likely in these cases, the time spent sending and saving data to persistent storage is much greater than the computational effort. The factorialize and factorialize-2 methods do not lend themselves to this as they do not generate complete tuples until all of the tuples are created. factorialize-4 creates complete tuples one at at time and these can be acted on immediately without waiting for all of the output to be generated.

Monday, May 6, 2019

Common Lisp Pipes→Enumerators

I've been plodding along through Peter Norvig's Paradigm's in Artificial Intelligence Programming and finding some helpful tips along the way. I especially enjoyed the discussion of pipes in chapter 9. I was intrigued by the pipes implementation of the sieve or Eratosthenes and wondered what else I could do with pipes.

Something that I didn't like about pipes is that they keep around a list of the results, when maybe you are actually done with those results. I was taking combinations of factors to produce a factorial experimental design and I decided to do so using something I have called multiple-radix-counting. Multiple-radix, meaning the radix (or base) for each digit is different. Imagine an odometer where each spinner has a different number of marks on it. Instead of always going from 0 to 9, some of the spinners might be 0 to 1 or 0 to 5. The sequencing is the same. When you get the end of the right most spinner, you wrap around on the first and advance the next spinner. Here's some sample output the shows the pattern:



I have taken the definitions from PIAP and modified them to avoid hanging on to the part of the pipe that has gone past. I have renamed certain functions/macros and made minor changes to them to produce a result that behaves differently. Instead of make-pipe, I have make-enumerator, although the macro is identical. It's important that I have a new name for this macro that suits the different usage in case I decide to implement enumerators differently in the future. The function get-current and get-next are the replacements for head and tail, respectively.  The representation is very similar to that used for pipes. I use a cons cell where the car is the current value and the cdr is a function call.



The purpose of the extract-by-multiple-radix is to take a selection out of source (an array of arrays) based on the indices that were specified. If you were doing multiplication of multiple term factors, you would take the terms by index from each factor and multiply them. To get the factors based on index, you would use extract-by-multiple-radix.