# Re: Factor

Factor: the language, the theory, and the practice.

## GetPercentageRounds

Thursday, January 19, 2023

There was a funny post on Twitter a couple of days ago about a recent event where the “Dutch government was forced to release the source code of their DigiD digital authentication iOS app” with this piece of C# code:

Some very funny discussions continued, with comments about how good or bad this code is, and how one might rewrite it in various ways. I thought it would be a fun opportunity to show a few variations of this simple function in Factor.

## Implementations

A direct translation of this code, might use cond which is basically a sequence of if statements:

``````: get-percentage-rounds ( percentage -- str )
{
{ [ dup 0.0 <= ] [ drop "⚪⚪⚪⚪⚪⚪⚪⚪⚪⚪" ] }
{ [ dup 0.0 0.1 between? ] [ drop "🔵⚪⚪⚪⚪⚪⚪⚪⚪⚪" ] }
{ [ dup 0.1 0.2 between? ] [ drop "🔵🔵⚪⚪⚪⚪⚪⚪⚪⚪" ] }
{ [ dup 0.2 0.3 between? ] [ drop "🔵🔵🔵⚪⚪⚪⚪⚪⚪⚪" ] }
{ [ dup 0.3 0.4 between? ] [ drop "🔵🔵🔵🔵⚪⚪⚪⚪⚪⚪" ] }
{ [ dup 0.4 0.5 between? ] [ drop "🔵🔵🔵🔵🔵⚪⚪⚪⚪⚪" ] }
{ [ dup 0.5 0.6 between? ] [ drop "🔵🔵🔵🔵🔵🔵⚪⚪⚪⚪" ] }
{ [ dup 0.6 0.7 between? ] [ drop "🔵🔵🔵🔵🔵🔵🔵⚪⚪⚪" ] }
{ [ dup 0.7 0.8 between? ] [ drop "🔵🔵🔵🔵🔵🔵🔵🔵⚪⚪" ] }
{ [ dup 0.8 0.9 between? ] [ drop "🔵🔵🔵🔵🔵🔵🔵🔵🔵⚪" ] }
[ drop "🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵" ]
} cond ;
``````

But since this is a series of if statements checked sequentially, you can just check the upper bounds. And since we only care about the argument for the comparison, we can use cond-case:

``````: get-percentage-rounds ( percentage -- str )
{
{ [ 0.0 <= ] [ "⚪⚪⚪⚪⚪⚪⚪⚪⚪⚪" ] }
{ [ 0.1 <= ] [ "🔵⚪⚪⚪⚪⚪⚪⚪⚪⚪" ] }
{ [ 0.2 <= ] [ "🔵🔵⚪⚪⚪⚪⚪⚪⚪⚪" ] }
{ [ 0.3 <= ] [ "🔵🔵🔵⚪⚪⚪⚪⚪⚪⚪" ] }
{ [ 0.4 <= ] [ "🔵🔵🔵🔵⚪⚪⚪⚪⚪⚪" ] }
{ [ 0.5 <= ] [ "🔵🔵🔵🔵🔵⚪⚪⚪⚪⚪" ] }
{ [ 0.6 <= ] [ "🔵🔵🔵🔵🔵🔵⚪⚪⚪⚪" ] }
{ [ 0.7 <= ] [ "🔵🔵🔵🔵🔵🔵🔵⚪⚪⚪" ] }
{ [ 0.8 <= ] [ "🔵🔵🔵🔵🔵🔵🔵🔵⚪⚪" ] }
{ [ 0.9 <= ] [ "🔵🔵🔵🔵🔵🔵🔵🔵🔵⚪" ] }
[ drop "🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵" ]
} cond-case ;
``````

One suggestion was to generate a substring based on the input – with the somewhat negative aspect that it allocates memory for the returned string when called:

``````: get-percentage-rounds ( percentage -- str )
10 * 10 swap - >integer dup 10 +
"🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵⚪⚪⚪⚪⚪⚪⚪⚪⚪⚪" subseq ;
``````

But another way would be to just index into the possible results, using quoted words to reduce the amount of tokens involved – resulting in this fairly aesthetic result:

``````: get-percentage-rounds ( percentage -- str )
10 * ceiling >integer qw{
⚪⚪⚪⚪⚪⚪⚪⚪⚪⚪
🔵⚪⚪⚪⚪⚪⚪⚪⚪⚪
🔵🔵⚪⚪⚪⚪⚪⚪⚪⚪
🔵🔵🔵⚪⚪⚪⚪⚪⚪⚪
🔵🔵🔵🔵⚪⚪⚪⚪⚪⚪
🔵🔵🔵🔵🔵⚪⚪⚪⚪⚪
🔵🔵🔵🔵🔵🔵⚪⚪⚪⚪
🔵🔵🔵🔵🔵🔵🔵⚪⚪⚪
🔵🔵🔵🔵🔵🔵🔵🔵⚪⚪
🔵🔵🔵🔵🔵🔵🔵🔵🔵⚪
🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵
} nth ;
``````

It’s always fun to see different ways to solve problems. In the Twitter thread, that includes using binary search, building the output character-by-character, generating solutions using ChatGPT, one-liners in Python, pattern matching, unit testing, and discussions of edge cases and naming conventions.