BookmarkSubscribeRSS Feed
RichardDeVen
Barite | Level 11

"The forgotten number system" - Numberphile

"The Cistercian Numerals was created in the middle ages as a number system." - Crack the code

 

There is one glyph for each number from 0 to 9999...  Hmm, perfect alignment for a double byte encoding (codelen=2) that would be rendered by a custom font built with PROC GFONT.

cisterci_digits.png

 

Build the custom font and a custom format

/* The forgotten number system 
 * https://www.youtube.com/watch?v=9p55Qgt7Ciw&ab_channel=Numberphile
 * 
 * The 4 digit number notation of the Order of Cistercians
 *
 * Coded: Richard DeVenezia, Nov 2020
 */

/*

 Digit glyphs, https://www.youtube.com/watch?v=9p55Qgt7Ciw&t=463s 


  10  |   1
  ____|____
      |
 1000 | 100


  1's place glyph parts
        _                   _         _         _
  |    |    |_   |\   |/   |/   | |  | |  |_|  |_|
  |    |    |    |    |    |    |    |    |    |
  |    |    |    |    |    |    |    |    |    |
  |    |    |    |    |    |    |    |    |    |
                           
  0    1    2    3    4    5    6    7    8    9

  Tidbit: glyphs of combination
  5 = 1 + 4
  7 = 1 + 6
  8 = 2 + 6
  9 = 1 + 2 + 6

  10's  place mirrors 1's in y 
  100's place mirrors 1's in x
  1000's place mirrors 1's in x and y
                          
*/

data strokes;
  length digit 8;
  input digit x1 y1 x2 y2;
  datalines;
1 1 4 2 4
2 1 3 2 3
3 1 4 2 3
4 1 3 2 4
5 1 3 2 4
5 1 4 2 4
6 2 4 2 3
7 1 4 2 4
7 2 4 2 3
8 1 3 2 3
8 2 3 2 4
9 1 4 2 4
9 2 4 2 3
9 2 3 1 3
;

data fontdata;
  declare hash strokes (dataset:'strokes', multidata:'yes', ordered:'yes');
  strokes.defineKey('digit');
  strokes.defineData('x1', 'y1', 'x2', 'y2');
  strokes.defineDone();
  call missing (digit, x1, y1, x2, y2);

  do n = 0 to 9999;
    k = n;
    d3 = int ( k / 1000 ); k + ( -d3 * 1000 );
    d2 = int ( k / 100 ) ; k + ( -d2 * 100 );
    d1 = int ( k / 10 );   k + ( -d1 * 10 );
    d0 = k;

   /*
    * mapping of decimal digits to hex nibbles (BCD'ish), offset digit by 03x to avoid 'unprintables`
    * digit nibble
    *   0    3
    *   1    4
    *   2    5
    *   3    6
    *   4    7
    *   5    8
    *   6    9
    *   7    A
    *   8    B
    *   9    C
    *
    * 4 nibbles -> 2 bytes -> 2 characters (i.e. a codelen=2 char in GFONT parlance)
    */

    c1 = byte((d3+3)*16+d2+3); /* 1000's and 100's */
    c2 = byte((d1+3)*16+d0+3); /* 10's and 1's */

    char = c1||c2;

    segment = 1; 
    x = 1; y = 0; output;
    x = 1; y = 4; output; 

    do while ( strokes.do_over(key:d0) = 0 );  /* 1's */
      segment + 1;
      x = x1; y = y1; output;
      x = x2; y = y2; output;
    end;

    do while ( strokes.do_over(key:d1) = 0 );  /* 10's */
      segment + 1;
      x = 2 - x1; y = y1; output;
      x = 2 - x2; y = y2; output;
    end;
                  
    do while ( strokes.do_over(key:d2) = 0 );  /* 100's */
      segment + 1;
      x = x1; y = 4 - y1; output;
      x = x2; y = 4 - y2; output;
    end;

    do while ( strokes.do_over(key:d3) = 0 );  /* 1000's */
      segment + 1;
      x = 2 - x1; y = 4 - y1; output;
      x = 2 - x2; y = 4 - y2; output;
    end;
  end;

  stop;

  format _numeric_ 4.;
  drop k d0-d3 digit x1 y1 x2 y2;
run;

data formatdata;
  set fontdata;
  by n;
  if first.n;
  retain fmtname 'cisterci';
  rename n=start char=label;
  keep n char fmtname;
run;

%if %sysfunc(libref(GFONT0)) ne 0 %then %do;
libname gfont0 "%sysfunc(pathname(WORK))";
%end;

proc gfont nodisplay data=fontdata name=cisterci resolution=2 uniform codelen=2;
proc format cntlin=formatdata;
run;

 

Show the digits

%macro cistercian_digits;
  data anno_digits;
    length style color $8 size 8 text $10;
    retain  xsys ysys '5' function 'LABEL';

    do _r = 1 to 4;
      z = 10**(_r-1);

      x = 0;
      y = 100 - _r * 100/5;  * placement of row labels 20% 40% 60% 80%;

      size = 1.25;
      style = '';
      position = '>';
      text = catx("'",z,"s");   * row labels 1's, 10's, 100's, 1000's;

      output;

      do _c = 1 to 10;    

        x = _c * 100/11 + 2;

        size = 4;
        style = 'CISTERCI';
        position = '+';

        text = put ( z * (_c-1), cisterci.);

        y = 100 - _r * 100/5;

        output;

        size = 1.25;
        style = '';                 * clear style, reset font to ftext= session option;
        y = 100 - _r * 100/5 - 7;
        color = 'grey';

        output;

        text = put(text, $hex4.);
        size = .9;
        y = y - 2;
        color = 'grey';

        output;

        color = '';
      end;
    end;

    * column labels at bottom of image (digits 0 to 9);

    y = 5;
    do _c = 1 to 10;
      x = _c * 100/11 + 2;
      size = 1.25;
      style = '';
      text = cats(_c-1);
      output;
    end;

    format z x y size 6.2;

    drop _:;
  run;

  filename cisterci 'cistercian_digits.png';
  goptions xpixels=600 ypixels=600 gsfname=cisterci ftext="Arial" goutmode=replace;

  ods html file='Cistercian_digits.html' nogtitle;

  proc ganno anno=anno_digits name="Cistercian Digits" gout=work.gseg;
    title1 h=12pt f='Arial' "Custom font - $2. and $HEX4. mappings to glyphs for a single";
    title2 h=12pt f='Arial' "Cistercian digit at 0, 1, 2, and 3 powers of 10";
  run;

  ods html close;
%mend cistercian_digits;

%cistercian_digits;

 

All the numbers

Download attached program for macro.

%cistercian_numbers (page_having=sum(num in (2020)) > 0);

RichardADeVenezia_0-1605556059513.png

 

1 REPLY 1
How to Concatenate Values

Learn how use the CAT functions in SAS to join values from multiple variables into a single value.

Find more tutorials on the SAS Users YouTube channel.

SAS Training: Just a Click Away

 Ready to level-up your skills? Choose your own adventure.

Browse our catalog!

Discussion stats
  • 1 reply
  • 1508 views
  • 11 likes
  • 2 in conversation