const char * UsageLines [] = {
       "p6addgraticule: Adds horizontal and vertical lines with labels",
       "    at sides, based on specified edges and intervals",
       "Reads P6 PPM from standard input, writes to standard output.",
       "",
       "Takes 6 arguments, all integers:",
       "    bottom",
       "    left",
       "    right",
       "    top",
       "    across interval (between vertical lines)",
       "    up interval (between horizontal lines)",
       "",
       "The labels on the horizontal lines will be interpolated",
       "    between the specified top and bottom edge values.",
       "",
       "The labels on the vertical lines will be interpolated",
       "    between the specified left and right edge values.",
       "",
       "April 2, 2022.  For latest, see:",
       "    gopher://sdf.org/1/users/julianbr",
       };
const int NumUsageLines = sizeof (UsageLines) / sizeof (UsageLines [0] );

#define NUM_ROWS 9
#define MIDDLE_ROW 4
#define NUM_COLUMNS 5
#define SPACING 1
#define MARGIN 30
#define MAX_DIGITS_ACROSS 8
#define MAX_DIGITS_UP 7
#define HORIZONTAL_R 255
#define HORIZONTAL_G 0
#define HORIZONTAL_B 0
#define VERTICAL_R 0
#define VERTICAL_G 0
#define VERTICAL_B 255

const int (DigitOutlines [] ) [NUM_ROWS] = {
       {14, 17, 17, 17, 17, 17, 17, 17, 14 },
       {12, 4, 4, 4, 4, 4, 4, 4, 14 },
       {14, 17, 1, 1, 1, 1, 14, 16, 31 },
       {14, 17, 1, 1, 14, 1, 1, 17, 14 },
       {6, 10, 10, 18, 31, 2, 2, 2, 2 },
       {31, 16, 16, 16, 30, 1, 1, 17, 14 },
       {14, 17, 16, 16, 30, 17, 17, 17, 14 },
       {15, 17, 1, 2, 2, 2, 4, 4, 4 },
       {14, 17, 17, 17, 14, 17, 17, 17, 14 },
       {14, 17, 17, 17, 15, 1, 1, 1, 2 },
       };

#include <stdio.h>

void AddGraticule (
               int bottom, int left, int right, int top,
               int AcrossInterval, int UpInterval)
       {
       int i, j, k, r, g, b, width, height, maxval, NumDigitsAcross, NumDigitsUp;
       int Limit, start, finish, across, up;
       int AcrossOffset, UpOffset, DistanceAcross, DistanceUp;
       int HorizontalClearance, VerticalClearance;
       int MinLeft, MaxLeft, MinBottom, MaxBottom;
       int MinRight, MaxRight, MinTop, MaxTop;
       int DigitNum, Label, Row, Column, Multiple;
       int PrintHorizontalLine, PrintVerticalLine, ProtectFromLine, AlreadyPrinted;
       int LeftLabel, RightLabel, TopLabel, BottomLabel;

       if (
                       getchar () != 'P'
                       || getchar () != '6'
                       || getchar () != '\n'
                       || scanf ("%d %d", & width, & height) != 2
                       || getchar () != '\n'
                       || scanf ("%d", & maxval) != 1
                       || maxval != 255
                       || getchar () != '\n'
                       ) {
               fprintf (stderr, "***p6addgraticule: Improper input file, must be");
               fprintf (stderr, " P6 image maxval 255\n");
               return;
               }

       NumDigitsAcross = 1;
       Limit = 10;
       while (NumDigitsAcross < MAX_DIGITS_ACROSS
                       && (left <= - Limit || right >= Limit) ) {
               NumDigitsAcross++;
               Limit *= 10;
               }
       NumDigitsUp = 1;
       Limit = 10;
       while (NumDigitsUp < MAX_DIGITS_UP
                       && (bottom <= - Limit || top >= Limit) ) {
               NumDigitsUp++;
               Limit *= 10;
               }
       AcrossOffset = left/AcrossInterval*AcrossInterval;
       if (left < AcrossInterval)
               AcrossOffset = - (AcrossInterval - left - 1)/AcrossInterval*AcrossInterval;
       UpOffset = bottom/UpInterval*UpInterval;
       if (bottom < UpInterval)
               UpOffset = - (UpInterval - bottom - 1)/UpInterval*UpInterval;
       DistanceAcross = right - left;
       DistanceUp = top - bottom;
       VerticalClearance = NumDigitsAcross*(NUM_COLUMNS + SPACING);
       HorizontalClearance = NumDigitsUp*(NUM_COLUMNS + SPACING);
       j = MARGIN + MIDDLE_ROW - 1;
       MinBottom = (left + j*DistanceAcross/width - AcrossOffset)/AcrossInterval;
       j = width - MARGIN - HorizontalClearance - NUM_ROWS + MIDDLE_ROW;
       MaxBottom = (left + j*DistanceAcross/width - AcrossOffset)/AcrossInterval;
       i = MARGIN + VerticalClearance + NUM_ROWS - MIDDLE_ROW - 1;
       MinLeft = (bottom + i*DistanceUp/height - UpOffset)/UpInterval;
       i = height - MARGIN - MIDDLE_ROW;
       MaxLeft = (bottom + i*DistanceUp/height - UpOffset)/UpInterval;
       i = MARGIN + NUM_ROWS - MIDDLE_ROW - 1;
       MinRight = (bottom + i*DistanceUp/height - UpOffset)/UpInterval;
       i = height - VerticalClearance - MARGIN - MIDDLE_ROW - 1;
       MaxRight = (bottom + i*DistanceUp/height - UpOffset)/UpInterval;
       j = MARGIN + HorizontalClearance + MIDDLE_ROW - 1;
       MinTop = (left + j*DistanceAcross/width - AcrossOffset)/AcrossInterval;
       j = width - MARGIN - NUM_ROWS + MIDDLE_ROW;
       MaxTop = (left + j*DistanceAcross/width - AcrossOffset)/AcrossInterval;

       printf ("P6\n%d %d\n%d\n", width, height, maxval);
       r = getchar ();
       for (i = height - 1; i >= 0; i--) {
               for (j = 0; j < width; j++) {
                       if (r == EOF)
                               g = EOF;
                       else
                               g = getchar ();
                       if (g == EOF)
                               b = EOF;
                       else
                               b = getchar ();

                       ProtectFromLine = 0;
                       AlreadyPrinted = 0;

                       /* Label for Horizontal Line */
                       DigitNum = -1;
                       LeftLabel = 0;
                       RightLabel = 0;
                       if (j >= MARGIN - SPACING && j < MARGIN + HorizontalClearance) {
                               if (j >= MARGIN) {
                                       DigitNum = (j - MARGIN)/(NUM_COLUMNS + SPACING);
                                       Column = (j - MARGIN)%(NUM_COLUMNS + SPACING);
                                       }
                               LeftLabel = 1;
                               }
                       else if (j >= width - MARGIN - HorizontalClearance
                                               && j < width - MARGIN + SPACING) {
                               if (j >= width - MARGIN - HorizontalClearance + SPACING) {
                                       DigitNum = (j - width + MARGIN + HorizontalClearance - SPACING)
                                                               /(NUM_COLUMNS + SPACING);
                                       Column = (j - width + MARGIN + HorizontalClearance - SPACING)
                                                               %(NUM_COLUMNS + SPACING);
                                       }
                               RightLabel = 1;
                               }
                       if (LeftLabel || RightLabel) {
                               Multiple = 1;
                               for (k = DigitNum + 1; k < NumDigitsUp; k++)
                                       Multiple *= 10;
                               Row = - SPACING - 1;
                               up = i + MIDDLE_ROW - NUM_ROWS + Row + 1;
                               start = (bottom + up*DistanceUp/height - UpOffset)/UpInterval;
                               Row++;
                               up = i + MIDDLE_ROW - NUM_ROWS + Row + 1;
                               finish = (bottom + up*DistanceUp/height - UpOffset)/UpInterval;
                               while (Row < NUM_ROWS + SPACING && finish <= start) {
                                       Row++;
                                       up = i + MIDDLE_ROW - NUM_ROWS + Row + 1;
                                       finish = (bottom + up*DistanceUp/height - UpOffset)/UpInterval;
                                       }
                               }
                       if (!AlreadyPrinted &&
                                       ((LeftLabel && finish > MinLeft && finish <= MaxLeft)
                                       || (RightLabel && finish > MinRight && finish <= MaxRight) )
                               ) {
                               if (Row < NUM_ROWS + SPACING)
                                       ProtectFromLine = 1;
                               if (Row >= 0 && Row < NUM_ROWS
                                               && DigitNum >= 0 && DigitNum < NumDigitsUp
                                               && Column < NUM_COLUMNS) {
                                       Label = finish*UpInterval + UpOffset;
                                       if (Label < 0)
                                               Label = - Label;
                                       if ((DigitOutlines [Label/Multiple%10] [Row] )
                                                       & (1 << (NUM_COLUMNS - Column - 1) ) ) {
                                               putchar (HORIZONTAL_R);
                                               putchar (HORIZONTAL_G);
                                               putchar (HORIZONTAL_B);
                                               AlreadyPrinted = 1;
                                               }
                                       }
                               }

                       /* Label for Vertical Line */
                       DigitNum = -1;
                       BottomLabel = 0;
                       TopLabel = 0;
                       if (i >= MARGIN - SPACING
                                       && i < MARGIN + VerticalClearance) {
                               if (i >= MARGIN) {
                                       DigitNum = (i - MARGIN)/(NUM_COLUMNS + SPACING);
                                       Column = (i - MARGIN)%(NUM_COLUMNS + SPACING);
                                       }
                               BottomLabel = 1;
                               }
                       else if (i >= height - MARGIN - VerticalClearance
                                       && i < height - MARGIN + SPACING) {
                               if (i >= height - MARGIN - VerticalClearance + SPACING) {
                                       DigitNum = (i - height + MARGIN + VerticalClearance - SPACING)
                                                       /(NUM_COLUMNS + SPACING);
                                       Column = (i - height + MARGIN + VerticalClearance - SPACING)
                                                       %(NUM_COLUMNS + SPACING);
                                       }
                               TopLabel = 1;
                               }
                       if (BottomLabel || TopLabel) {
                               Multiple = 1;
                               for (k = DigitNum + 1; k < NumDigitsAcross; k++)
                                       Multiple *= 10;
                               Row = NUM_ROWS + SPACING;
                               across = j + MIDDLE_ROW - Row;
                               start = (left + across*DistanceAcross/width - AcrossOffset)/AcrossInterval;
                               Row--;
                               across = j + MIDDLE_ROW - Row;
                               finish = (left + across*DistanceAcross/width - AcrossOffset)/AcrossInterval;
                               while (Row > - SPACING - 1 && finish <= start) {
                                       Row--;
                                       across = j + MIDDLE_ROW - Row;
                                       finish = (left + across*DistanceAcross/width - AcrossOffset)/AcrossInterval;
                                       }
                               }
                       if (!AlreadyPrinted && (
                                       (BottomLabel && finish > MinBottom && finish <= MaxBottom)
                                       || (TopLabel && finish > MinTop && finish <= MaxTop) )
                               ) {
                               if (Row >= - SPACING)
                                       ProtectFromLine = 1;
                               if (Row >= 0 && Row < NUM_ROWS
                                               && DigitNum >= 0 && DigitNum < NumDigitsAcross
                                               && Column < NUM_COLUMNS) {
                                       Label = finish*AcrossInterval + AcrossOffset;
                                       if (Label < 0)
                                               Label = - Label;
                                       if ((DigitOutlines [Label/Multiple%10] [Row] )
                                                       & (1 << (NUM_COLUMNS - Column - 1) ) ) {
                                               putchar (VERTICAL_R);
                                               putchar (VERTICAL_G);
                                               putchar (VERTICAL_B);
                                               AlreadyPrinted = 1;
                                               }
                                       }
                               }

                       if (!ProtectFromLine && !AlreadyPrinted) {
                               PrintHorizontalLine = 0;
                               PrintVerticalLine = 0;
                               if ((bottom + i*DistanceUp/height - UpOffset)/UpInterval
                               > (bottom + (i - 1 )*DistanceUp/height - UpOffset)/UpInterval)
                                       PrintHorizontalLine = 1;
                               if ((left + j*DistanceAcross/width - AcrossOffset)/AcrossInterval
                               > (left + (j - 1)*DistanceAcross/width - AcrossOffset)/AcrossInterval)
                                       PrintVerticalLine = 1;
                               if (PrintHorizontalLine && !PrintVerticalLine) {
                                       putchar (HORIZONTAL_R);
                                       putchar (HORIZONTAL_G);
                                       putchar (HORIZONTAL_B);
                                       AlreadyPrinted = 1;
                                       }
                               if (PrintVerticalLine && !PrintHorizontalLine) {
                                       putchar (VERTICAL_R);
                                       putchar (VERTICAL_G);
                                       putchar (VERTICAL_B);
                                       AlreadyPrinted = 1;
                                       }
                               }
                       if (!AlreadyPrinted) {
                               putchar (r);
                               putchar (g);
                               putchar (b);
                               }

                       if (b == EOF)
                               r = EOF;
                       else
                               r = getchar ();
                       }
               }
       if (r != EOF)
               fprintf (stderr, "***p6addgraticule: Too much image data.\n");
       if (b == EOF)
               fprintf (stderr, "***p6addgraticule: Not enough image data.\n");
       }

int main (int argc, char * argv [] )
       {
       char c;
       int i, ok;
       int bottom, left, right, top, AcrossInterval, UpInterval;

       if (argc == 1) {
               for (i = 0; i < NumUsageLines; i++)
                       printf ("%s\n", UsageLines [i] );
               }
       else if (argc != 7) {
               fprintf (stderr, "Usage: %s", argv [0] );
               fprintf (stderr, " (bottom) (left) (right) (top)\n");
               fprintf (stderr, "\t(interval across between vertical lines)\n");
               fprintf (stderr, "\t(interval down between horizontal lines)\n");
               }
       else {
               ok = 1;
               if (sscanf (argv [1], "%d%c", & bottom, & c) != 1) {
                       fprintf (stderr, "***p6addgraticule: Expecting number for bottom,");
                       fprintf (stderr, " found \"%s\".\n", argv [1] );
                       ok = 0;
                       }
               if (sscanf (argv [2], "%d%c", & left, & c) != 1) {
                       fprintf (stderr, "***p6addgraticule: Expecting number for left,");
                       fprintf (stderr, " found \"%s\".\n", argv [2] );
                       ok = 0;
                       }
               if (sscanf (argv [3], "%d%c", & right, & c) != 1) {
                       fprintf (stderr, "***p6addgraticule: Expecting number for right,");
                       fprintf (stderr, " found \"%s\".\n", argv [3] );
                       ok = 0;
                       }
               if (sscanf (argv [4], "%d%c", & top, & c) != 1) {
                       fprintf (stderr, "***p6addgraticule: Expecting number for top,");
                       fprintf (stderr, " found \"%s\".\n", argv [4] );
                       ok = 0;
                       }
               if (sscanf (argv [5], "%d%c", & AcrossInterval, & c) != 1) {
                       fprintf (stderr, "***p6addgraticule: Expecting number for across interval,");
                       fprintf (stderr, " found \"%s\".\n", argv [5] );
                       ok = 0;
                       }
               if (sscanf (argv [6], "%d%c", & UpInterval, & c) != 1) {
                       fprintf (stderr, "***p6addgraticule: Expecting number for up interval,");
                       fprintf (stderr, " found \"%s\".\n", argv [6] );
                       ok = 0;
                       }
               if (ok) {
                       if (left > right) {
                               left = - left;
                               right = - right;
                               }
                       if (bottom > top) {
                               bottom = - bottom;
                               top = - top;
                               }
                       AddGraticule (bottom, left, right, top, AcrossInterval, UpInterval);
                       }
               }

       return 0;
       }