An explanation is here (in interlingua).


/* 20-24 October 2020, by Ruud Harmsen

   Idea: take 12 steps in an octave, give them Zarlino-like
   ratios consisting of rather low numerators and denominators
   containing factors 2, 3 and 5 (optionally more). Then
   calculate the ratios (expressed as whole numbers) between
   those steps. In other words, shift the scale, start it at
   different points. Would the derived scales be usable
   musically, and would they contain bad, dissonant intervals,
   like "wolf fifths"?
 */

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <ctype.h>

typedef short int factor_t;
typedef short int numfac_t;

static const factor_t factors[] = {1, 2, 3, 5, 7, 11, 13, 17, -1};
#define SizeFactors (sizeof factors / sizeof factors[0])

struct ratio
{
   numfac_t *num_factors_numerator;   /* Points to foot of malloc'ed array, ends in -1 */
   numfac_t *num_factors_denominator; /* Points to foot of malloc'ed array, ends in -1 */
};
static struct ratio notes[12];
#define SizeNotes (sizeof notes / sizeof notes[0])

static void free_all (void);
static int read_infile (FILE *inf);
static int fillfactor (char *cp, int note_index, int *p_allocd, int is_numerator);
static int report (void);
static void report_one (int i, int j);
static int report_width = 4;

#ifdef DEBUG
static void show_table (void);
static void ChkIntegr (int linenumber);
#endif

int main (int argc, char **argv)
{
   FILE *inf = NULL;

   if (argc < 2)
   {
      fprintf(stderr, "Usage: %s infile\n", argv[0]);
      exit(1);
   }
   if (!(inf = fopen(argv[1], "r")))
   {
      fprintf(stderr, "%s: Can't open input file %s\n", argv[0], argv[1]);
      exit(2);
   }

   read_infile(inf);
   #ifdef DEBUG
      ChkIntegr (__LINE__);
   #endif
   report();
   free_all();

   return 0;
}

static void free_all (void)
{
   int note_index;

   for (note_index = 0; note_index < SizeNotes; note_index++)
   {
      if (notes[note_index].num_factors_numerator)
      {
         free(notes[note_index].num_factors_numerator);
         notes[note_index].num_factors_numerator = NULL;
      }
      if (notes[note_index].num_factors_denominator)
      {
         free(notes[note_index].num_factors_denominator);
         notes[note_index].num_factors_denominator = NULL;
      }
   }
}

/* See examples of such a file in this same directory, e.g. inzar1. */
static int read_infile (FILE *inf)
{
   char line[81];
   char *cp;
   char is_numerator; /* else denominator */
   int note_index;
   int factors_found;

   for (note_index = 0;
        note_index < SizeNotes && fgets(line, sizeof line, inf);
        note_index += factors_found)
   {
      int allocd = 0;

      is_numerator = 1;
      factors_found = 0;
      for (cp = line; cp < line + sizeof line && *cp && *cp != '#'; cp++)
      {
         if (*cp == '/' || *cp == ':')
         {
            is_numerator = !is_numerator;
            allocd = 0;
         }
         else if (isdigit(*cp))
         {
            cp += fillfactor (cp, note_index, &allocd, is_numerator);
            factors_found = 1;
         }
         /* Whitespace, and anything else: all ignored */
      }
   }

   fclose(inf);

   return 0;
}

/* Build up a table of prime factors for the ratios, in memory,
   for later reference. */
static int fillfactor (char *cp, int note_index, int *p_allocd, int is_numerator)
{
   int i;
   int wherenewcount = -1;
   int factor;
   int consumed;

   sscanf(cp, "%3d%n", &factor, &consumed);

   for (i = 0; factors[i] > 0; i++)
   {
      if (factors[i] == factor)
      {
         wherenewcount = i;
         break;
      }
   }
   if (wherenewcount < 0)
      /* Factor not found. Ignore, do nothing. */
      return 2;

   if (wherenewcount + 2 > *p_allocd)
   {
      numfac_t *p;

      if (is_numerator)
      {
         /* Make room for more factor counters, as necessary. */
         p = realloc(notes[note_index].num_factors_numerator,
            (wherenewcount + 2) * sizeof (numfac_t));

         if (p)
         {
            int zerofrom = *p_allocd - 1;
            if (zerofrom < 0) zerofrom = 0;

            notes[note_index].num_factors_numerator = p;
            for (i = zerofrom; i <= wherenewcount; i++)
            {
               notes[note_index].num_factors_numerator[i] = 0;
            }

            notes[note_index].num_factors_numerator[wherenewcount + 1] = -1;
            *p_allocd = wherenewcount + 2;
         }
         else
         {
            /* A failed realloc is so unlikely, seeing the minute
               amounts of memory required, that we simply abort the
               program then. */
            fprintf(stderr, "%s line %d: cannot realloc %d bytes\n",
               __FILE__, __LINE__, (int)((wherenewcount + 2) * sizeof (numfac_t)));
            exit(2);
         }
      }
      else
      {
         p = realloc(notes[note_index].num_factors_denominator,
            (wherenewcount + 2) * sizeof (numfac_t));

         if (p)
         {
            int zerofrom = *p_allocd - 1;
            if (zerofrom < 0) zerofrom = 0;

            notes[note_index].num_factors_denominator = p;
            for (i = zerofrom; i <= wherenewcount; i++)
               notes[note_index].num_factors_denominator[i] = 0;

            notes[note_index].num_factors_denominator[wherenewcount + 1] = -1;
            *p_allocd = wherenewcount + 2;
         }
         else
         {
            /* A failed realloc is so unlikely, seeing the minute
               amounts of memory required, that we simply abort the
               program then. */
            fprintf(stderr, "%s line %d: cannot realloc %d bytes\n",
               __FILE__, __LINE__, (int)((wherenewcount + 2) * sizeof (numfac_t)));
            exit(2);
         }
      }
   }

   if (is_numerator)
      notes[note_index].num_factors_numerator[wherenewcount] += 1;
   else
      notes[note_index].num_factors_denominator[wherenewcount] += 1;

   return consumed - 1; /* Minus one to compensate for cp++ in calling function */
}

/* Print the result to stdout, in two different ways. */
static int report (void)
{
   int i, j;

   #ifdef DEBUG
   show_table();
   #endif

   for (i = 0; i <= SizeNotes; i++)
   {
      fprintf(stdout, "\n%2d ", i);
      for (j = 0; j < SizeNotes; j++)
         report_one(j, i);
   }

   fprintf(stdout, "\n  ");
   for (j = 0; j <= SizeNotes; j++)
      fprintf(stdout, "%*s%2d%*s", report_width, "", j, report_width - 1, "");

   for (i = 0; i < SizeNotes; i++)
   {
      fprintf(stdout, "\n%2d ", i);
      for (j = i; j <= SizeNotes + i; j++)
         report_one(i, j);
   }
   fprintf(stdout, "\n\n");
   return 0;
}

static void report_one (int i, int j)
{
   int jj;
   numfac_t *numer_factors = NULL;
   numfac_t *denom_factors = NULL;
   numfac_t *n;
   int index;
   int numerator, denominator;

   if (!(numer_factors = calloc((SizeFactors-1), sizeof (numfac_t))))
   {
      /* A failed calloc is so unlikely, seeing the minute
         amounts of memory required, that we simply abort the
         program then. */
      fprintf(stdout, "%s line %d: cannot calloc %d bytes\n",
         __FILE__, __LINE__, (int)((SizeFactors-1) * sizeof (numfac_t)));
      exit(3);
   }
   if (!(denom_factors = calloc((SizeFactors-1), sizeof (numfac_t))))
   {
      fprintf(stdout, "%s line %d: cannot calloc %d bytes\n",
         __FILE__, __LINE__, (int)((SizeFactors-1) * sizeof (numfac_t)));
      exit(3);
   }

   jj = j;
   while (jj >= SizeNotes)
   {
      jj -= SizeNotes;
      /* Add a factor two in the numerator */
      numer_factors[1]++;
   }

   for (index = 0, n = notes[jj].num_factors_numerator  ;
        n && *n >= 0; n++, index++)
      numer_factors[index] += *n;
   for (index = 0, n = notes[i].num_factors_numerator  ;
        n && *n >= 0; n++, index++)
      denom_factors[index] += *n;
   for (index = 0, n = notes[jj].num_factors_denominator;
        n && *n >= 0; n++, index++)
      denom_factors[index] += *n;
   for (index = 0, n = notes[i].num_factors_denominator;
        n && *n >= 0; n++, index++)
      numer_factors[index] += *n;

   /* Simplify ratios, reducing common factors in numerator and denominator */
   for (index = 0; index < SizeFactors - 1; index++)
   {
      /* Number of factors is non-zero above and below? */
      if (numer_factors[index] && denom_factors[index])
      {
         numfac_t diff = numer_factors[index] - denom_factors[index];
         if (diff > 0)
         {
            numer_factors[index] -= denom_factors[index];
            denom_factors[index] = 0;
         }
         else
         {
            denom_factors[index] -= numer_factors[index];
            numer_factors[index] = 0;
         }
      }
   }

   /* Now calculate the resulting ratio, by multiplying all the factors,
      for both numerator and denominator. */
   numerator = 1;
   for (index = 0; index < SizeFactors - 1; index++)
      numerator   *= pow(factors[index], numer_factors[index]);

   denominator = 1;
   for (index = 0; index < SizeFactors - 1; index++)
      denominator *= pow(factors[index], denom_factors[index]);

   if (1 || numerator >= denominator)
      fprintf(stdout, "%*d:%-*d", report_width, numerator,
                                  report_width, denominator);
   else
      fprintf(stdout, "%*s-:-%*s", report_width - 1, "",
                                   report_width - 1, "");

   free(numer_factors);
   free(denom_factors);
}

#ifdef DEBUG
static void show_table (void)
{
   int j;
   numfac_t *n;

   for (j = 0; j < SizeNotes; j++)
   {
      fprintf(stdout, "\nj=%2d\t", j);
      for (n = notes[j].num_factors_numerator; n && *n > -1; n++)
      {
         int index = n - notes[j].num_factors_numerator;
         fprintf(stdout, "Numb.of factors %2d: %2d ",
            (int)factors[index],
            (int)notes[j].num_factors_numerator[index]);
      }
      fprintf(stdout, "\n\t");
      for (n = notes[j].num_factors_denominator; n && *n > -1; n++)
      {
         int index = n - notes[j].num_factors_denominator;
         fprintf(stdout, "Numb.of factors %2d: %2d ",
            (int)factors[index],
            (int)notes[j].num_factors_denominator[index]);
      }
      fprintf(stdout, "\n");
   }
   fprintf(stdout, "\n");
}
#endif

#ifdef DEBUG
static void ChkIntegr (int linenumber)
{
   int j;
   numfac_t *n;
   int index;
   static int NumErrors = 0;

   for (j = 0; j < SizeNotes; j++)
   {
      for (n = notes[j].num_factors_numerator, index = 0;
           n && *n != -1 && index < SizeFactors;
           n++, index++)
      {
         if (*n < -1 || *n > 10)
         {
            fprintf(stdout,
               "Checked from line %4d: Note=%d, factor %d %5d times in numerator\n",
               linenumber, j, index, (int)*n);
            if (++NumErrors > 150)
            {
               fprintf(stdout, "Too many errors\n\n");
               exit(10);
            }
         }
      }
      for (n = notes[j].num_factors_denominator, index = 0;
           n && *n != -1 && index < SizeFactors;
           n++, index++)
      {
         if (*n < -1 || *n > 10)
         {
            fprintf(stdout,
               "\nChecked from line %4d: Note=%d, factor %d %5d times in denominator\n",
               linenumber, j, index, (int)*n);
            if (++NumErrors > 150)
            {
               fprintf(stdout, "Too many errors\n\n");
               exit(11);
            }
         }
      }
   }
}
#endif