const char * UsageLines [] = {
	"Usage: p6orient (output width) (output height)",
	" (pivot point #1) (pivot point #2)",
	"Shifts, rotates, and resizes input image so that",
	" the two input pivots are positioned as",
	" specified on the output image.",
	"Output is cropped or padded (with gray) as needed",
	" to reach specified output dimensions.",
	"Reads P6 PPM image from standard input,",
	" writes PPM image to standard output.",
	"Each pivot point is of the form:",
	" (input across),(input down)=(output across),(output down)",
	" where the leftmost pixels are (- width/2) across,",
	" and the topmost pixels are (- height/2) down.",
	"For example, 0,0=0,0 matches the center of the input image",
	" with the center of the output image.",
	"Pixels are moved, repeated, or skipped to produce output",
	" image.  No recalculating of pixel values is done.",
	"With appropriate choice of pivot points, p6orient can be",
	" used to pad, crop, or resize an image.",
	"For example: 0,0=0,0 0,1=0,1",
	" will keep the center as the center and not resize,",
	" if the supplied output dimensions are smaller than the input",
	" it will crop, and,",
	" if the supplied output dimensions are larger than the input",
	" it will pad.",
	"November 24, 2011.  Newest is at gopher -p users/julianbr sdf.org",
	};
const int NumUsageLines = sizeof (UsageLines)/sizeof (UsageLines [0] );

#include <stdlib.h>
#include <stdio.h>
#include <string.h>


void OrientImage (
		char (* * InputPixels) [3],
		int InputWidth, int InputHeight,
		int OutputWidth, int OutputHeight,
		int InputPoint1Across, int InputPoint1Down,
		int OutputPoint1Across, int OutputPoint1Down,
		int InputPoint2Across, int InputPoint2Down,
		int OutputPoint2Across, int OutputPoint2Down)
	{
	char PaddingPixel [] = {200, 200, 200};
	int InputAcross, InputDown, OutputAcross, OutputDown;
	long int InputAcrossRemainder, InputDownRemainder;
	long int OutputDotOutput, OutputDotInput, OutputCrossInput;

	OutputDotOutput
			= (OutputPoint2Across - OutputPoint1Across)
				*(OutputPoint2Across - OutputPoint1Across)
			+ (OutputPoint2Down - OutputPoint1Down)
				*(OutputPoint2Down - OutputPoint1Down);
	OutputDotInput
			= (OutputPoint2Across - OutputPoint1Across)
				*(InputPoint2Across - InputPoint1Across)
			+ (OutputPoint2Down - OutputPoint1Down)
				*(InputPoint2Down - InputPoint1Down);
	OutputCrossInput
			= (OutputPoint2Down - OutputPoint1Down)
				*(InputPoint2Across - InputPoint1Across)
			- (OutputPoint2Across - OutputPoint1Across)
				*(InputPoint2Down - InputPoint1Down);

	printf ("P6\n");
	printf ("%d", OutputWidth);
	printf (" %d\n", OutputHeight);
	printf ("255\n");

	/* Start at pivot point 1 */
	OutputAcross = OutputPoint1Across;
	OutputDown = OutputPoint1Down;
	InputAcross = InputPoint1Across;
	InputAcrossRemainder = OutputDotOutput/2;
	InputDown = InputPoint1Down;
	InputDownRemainder = OutputDotOutput/2;

	/* Go to top edge */
	while (OutputDown < - OutputHeight/2) {
		InputAcrossRemainder += OutputCrossInput;
		while (InputAcrossRemainder < 0) {
			InputAcrossRemainder += OutputDotOutput;
			InputAcross--;
			}
		while (InputAcrossRemainder >= OutputDotOutput) {
			InputAcrossRemainder -= OutputDotOutput;
			InputAcross++;
			}
		InputDownRemainder += OutputDotInput;
		while (InputDownRemainder < 0) {
			InputDownRemainder += OutputDotOutput;
			InputDown--;
			}
		while (InputDownRemainder >= OutputDotOutput) {
			InputDownRemainder -= OutputDotOutput;
			InputDown++;
			}
		OutputDown++;
		}
	while (OutputDown > - OutputHeight/2) {
		InputAcrossRemainder -= OutputCrossInput;
		while (InputAcrossRemainder < 0) {
			InputAcrossRemainder += OutputDotOutput;
			InputAcross--;
			}
		while (InputAcrossRemainder >= OutputDotOutput) {
			InputAcrossRemainder -= OutputDotOutput;
			InputAcross++;
			}
		InputDownRemainder -= OutputDotInput;
		while (InputDownRemainder < 0) {
			InputDownRemainder += OutputDotOutput;
			InputDown--;
			}
		while (InputDownRemainder >= OutputDotOutput) {
			InputDownRemainder -= OutputDotOutput;
			InputDown++;
			}
		OutputDown--;
		}

	while (OutputDown < OutputHeight - OutputHeight/2) {
		/* Go to left edge */
		while (OutputAcross < - OutputWidth/2) {
			InputAcrossRemainder += OutputDotInput;
			while (InputAcrossRemainder < 0) {
				InputAcrossRemainder += OutputDotOutput;
				InputAcross--;
				}
			while (InputAcrossRemainder >= OutputDotOutput) {
				InputAcrossRemainder -= OutputDotOutput;
				InputAcross++;
				}
			InputDownRemainder -= OutputCrossInput;
			while (InputDownRemainder < 0) {
				InputDownRemainder += OutputDotOutput;
				InputDown--;
				}
			while (InputDownRemainder >= OutputDotOutput) {
				InputDownRemainder -= OutputDotOutput;
				InputDown++;
				}
			OutputAcross++;
			}
		while (OutputAcross > - OutputWidth/2) {
			InputAcrossRemainder -= OutputDotInput;
			while (InputAcrossRemainder < 0) {
				InputAcrossRemainder += OutputDotOutput;
				InputAcross--;
				}
			while (InputAcrossRemainder >= OutputDotOutput) {
				InputAcrossRemainder -= OutputDotOutput;
				InputAcross++;
				}
			InputDownRemainder += OutputCrossInput;
			while (InputDownRemainder < 0) {
				InputDownRemainder += OutputDotOutput;
				InputDown--;
				}
			while (InputDownRemainder >= OutputDotOutput) {
				InputDownRemainder -= OutputDotOutput;
				InputDown++;
				}
			OutputAcross--;
			}

		while (OutputAcross < OutputWidth - OutputWidth/2) {
			if (
					InputAcross >= - InputWidth/2
					&& InputAcross < InputWidth
						- InputWidth/2
					&& InputDown >= - InputHeight/2
					&& InputDown < InputHeight
						- InputHeight/2)
				fwrite (
					InputPixels [InputDown
						+ InputHeight/2]
						+ InputAcross
						+ InputWidth/2,
					sizeof (InputPixels [0] [0] ),
					1,
					stdout);
			else {
				fwrite (
					PaddingPixel,
					sizeof (InputPixels [0] [0] ),
					1,
					stdout);
				}
			InputAcrossRemainder += OutputDotInput;
			while (InputAcrossRemainder < 0) {
				InputAcrossRemainder += OutputDotOutput;
				InputAcross--;
				}
			while (InputAcrossRemainder >= OutputDotOutput) {
				InputAcrossRemainder -= OutputDotOutput;
				InputAcross++;
				}
			InputDownRemainder -= OutputCrossInput;
			while (InputDownRemainder < 0) {
				InputDownRemainder += OutputDotOutput;
				InputDown--;
				}
			while (InputDownRemainder >= OutputDotOutput) {
				InputDownRemainder -= OutputDotOutput;
				InputDown++;
				}
			OutputAcross++;
			}
		InputAcrossRemainder += OutputCrossInput;
		while (InputAcrossRemainder < 0) {
			InputAcrossRemainder += OutputDotOutput;
			InputAcross--;
			}
		while (InputAcrossRemainder >= OutputDotOutput) {
			InputAcrossRemainder -= OutputDotOutput;
			InputAcross++;
			}
		InputDownRemainder += OutputDotInput;
		while (InputDownRemainder < 0) {
			InputDownRemainder += OutputDotOutput;
			InputDown--;
			}
		while (InputDownRemainder >= OutputDotOutput) {
			InputDownRemainder -= OutputDotOutput;
			InputDown++;
			}
		OutputDown++;
		}
	}


int ReadImage (
		char (* * * PixelsPtr) [3],
		int * WidthPtr, int * HeightPtr)
	{
	int i, j, maxval;

	PixelsPtr [0] = NULL;
	if (getchar () != 'P'
			|| getchar () != '6'
			|| getchar () != '\n'
			|| scanf ("%d", WidthPtr) < 1
			|| scanf ("%d", HeightPtr) < 1
			|| scanf ("%d", & maxval) < 1
			|| WidthPtr [0] < 1
			|| HeightPtr [0] < 1
			|| maxval != 255
			|| getchar () != '\n') {
		fprintf (stderr, "***p6orient: Improper input type, not");
		fprintf (stderr, " P6 ppm image with maxval=255.\n");
		return 0;
		}
	PixelsPtr [0] = malloc (HeightPtr [0]*sizeof (PixelsPtr [0] [0] ) );
	if (PixelsPtr [0] == NULL) {
		fprintf (stderr, "***p6orient: Not enough memory.\n");
		return 0;
		}
	for (i = 0; i < HeightPtr [0]; i++)
		PixelsPtr [0] [i] = NULL;
	for (i = 0; i < HeightPtr [0]; i++) {
		PixelsPtr [0] [i] = malloc (
			WidthPtr [0]*sizeof (PixelsPtr [0] [0] [0] ) );
		if (PixelsPtr [0] [i] == NULL) {
			fprintf (stderr, "***p6orient: Not enough memory.\n");
			return 0;
			}
		}
	for (i = 0; i < HeightPtr [0]; i++) {
		for (j = 0; j < WidthPtr [0]; j++)
			memset (PixelsPtr [0] [i] [j], '.',
					sizeof (PixelsPtr [0] [0] [0] ) );
		}
	for (i = 0; i < HeightPtr [0]; i++) {
		for (j = 0; j < WidthPtr [0]; j++) {
			if (fread (PixelsPtr [0] [i] [j],
					sizeof (PixelsPtr [0] [0] [0] ),
					1,
					stdin) < 1) {
				fprintf (stderr, "***p6orient: Premature");
				fprintf (stderr, " end of input image");
				fprintf (stderr, " data.\n");
				return 1; /* proceed anyway */
				}
			}
		}
	if (getchar () != EOF)
		fprintf (stderr, "***p6orient: Improper extra input data.\n");
	return 1;
	}


void ClosePixels (
		char (* * Pixels) [3],
		unsigned int height)
	{
	unsigned int i;

	if (Pixels != NULL) {
		for (i = 0; i < height; i++) {
			if (Pixels [i] != NULL)
				free (Pixels [i] );
			}
		free (Pixels);
		}
	}
 

int ReadPivotPoint (
		char * point,
		int * InputAcrossPtr,
		int * InputDownPtr,
		int * OutputAcrossPtr,
		int * OutputDownPtr)
	{
	char * ptr;
	int negative;

	ptr = point;
	negative = 0;
	if (ptr [0] == '-') {
		negative = 1;
		ptr++;
		}
	InputAcrossPtr [0] = 0;
	while (ptr [0] >= '0' && ptr [0] <= '9') {
		InputAcrossPtr [0] = 10*InputAcrossPtr [0] + (ptr [0] - '0');
		ptr++;
		}
	if (negative)
		InputAcrossPtr [0] = - InputAcrossPtr [0];
	if (ptr [0] != ',') {
		fprintf (stderr, "***p6orient: Expecting comma, found");
		fprintf (stderr, " improper character: \"%s\".\n", point);
		return 0;
		}
	ptr++;
	negative = 0;
	if (ptr [0] == '-') {
		negative = 1;
		ptr++;
		}
	InputDownPtr [0] = 0;
	while (ptr [0] >= '0' && ptr [0] <= '9') {
		InputDownPtr [0] = 10*InputDownPtr [0] + (ptr [0] - '0');
		ptr++;
		}
	if (negative)
		InputDownPtr [0] = - InputDownPtr [0];
	if (ptr [0] != '=') {
		fprintf (stderr, "***p6orient: Expecting =, found improper");
		fprintf (stderr, " character: \"%s\".\n", point);
		return 0;
		}
	ptr++;
	negative = 0;
	if (ptr [0] == '-') {
		negative = 1;
		ptr++;
		}
	OutputAcrossPtr [0] = 0;
	while (ptr [0] >= '0' && ptr [0] <= '9') {
		OutputAcrossPtr [0] = 10*OutputAcrossPtr [0]
				+ (ptr [0] - '0');
		ptr++;
		}
	if (negative)
		OutputAcrossPtr [0] = - OutputAcrossPtr [0];
	if (ptr [0] != ',') {
		fprintf (stderr, "***p6orient: Expecting comma, found");
		fprintf (stderr, " improper character: \"%s\".\n", point);
		return 0;
		}
	ptr++;
	negative = 0;
	if (ptr [0] == '-') {
		negative = 1;
		ptr++;
		}
	OutputDownPtr [0] = 0;
	while (ptr [0] >= '0' && ptr [0] <= '9') {
		OutputDownPtr [0] = 10*OutputDownPtr [0] + (ptr [0] - '0');
		ptr++;
		}
	if (negative)
		OutputDownPtr [0] = - OutputDownPtr [0];
	if (ptr [0] != '\0') {
		fprintf (stderr, "***p6orient: Improper character(s) at");
		fprintf (stderr, " end: \"%s\".\n", point);
		return 0;
		}
	return 1;
	}


int main (int argc, char * argv [] )
	{
	char (* * InputPixels) [3];
	int InputWidth, InputHeight, OutputWidth, OutputHeight;
	int InputPoint1Across, InputPoint1Down;
	int InputPoint2Across, InputPoint2Down;
	int OutputPoint1Across, OutputPoint1Down;
	int OutputPoint2Across, OutputPoint2Down;
	int i, ok;

	if (argc < 2) {
		for (i = 0; i < NumUsageLines; i++)
			printf ("%s\n", UsageLines [i] );
		}
	else if (argc == 5) {
		ok = 1;
		if (sscanf (argv [1], "%d", & OutputWidth) != 1
				|| OutputWidth < 1) {
			fprintf (stderr, "***p6orient: Expecting output");
			fprintf (stderr, " width,");
			fprintf (stderr, " found \"%s\".\n", argv [1] );
			ok = 0;
			}
		if (sscanf (argv [2], "%d", & OutputHeight) != 1
				|| OutputHeight < 1) {
			fprintf (stderr, "***p6orient: Expecting output");
			fprintf (stderr, " height,");
			fprintf (stderr, " found \"%s\".\n", argv [2] );
			}
		if (!ReadPivotPoint (
				argv [3],
				& InputPoint1Across, & InputPoint1Down,
				& OutputPoint1Across, & OutputPoint1Down) )
			ok = 0;
		if (!ReadPivotPoint (
				argv [4],
				& InputPoint2Across, & InputPoint2Down,
				& OutputPoint2Across, & OutputPoint2Down) )
			ok = 0;
		if (ok) {
			if (OutputPoint1Across == OutputPoint2Across
				&& OutputPoint1Down == OutputPoint2Down) {
				fprintf (stderr, "***p6orient: Both pivot");
				fprintf (stderr, " points are at same");
				fprintf (stderr, " output location:");
				fprintf (stderr, " %d,", OutputPoint1Across);
				fprintf (stderr, "%d\n", OutputPoint1Down);
				ok = 0;
				}
			}
		if (ok) {
			if (ReadImage (
					& InputPixels,
					& InputWidth, & InputHeight) ) {
				OrientImage (
					InputPixels,
					InputWidth, InputHeight,
					OutputWidth, OutputHeight,
					InputPoint1Across, InputPoint1Down,
					OutputPoint1Across, OutputPoint1Down,
					InputPoint2Across, InputPoint2Down,
					OutputPoint2Across, OutputPoint2Down
					);
				ClosePixels (InputPixels, InputHeight);
				}
			else
				ClosePixels (InputPixels, InputHeight);
			}
		}
	else {
		fprintf (stderr, "Usage: p6orient");
		fprintf (stderr, " (output width)");
		fprintf (stderr, " (output height)");
		fprintf (stderr, " (pivot point #1)");
		fprintf (stderr, " (pivot point #2)\n");
		}
	return 0;
	}