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;
	}