Bulk Mailing
Difficulty: Hard

An organization that wishes to make a large mailing can save postage by following U.S. Postal Service rules for a bulk mailing. Letters in zip code order are bundled into packets of 10-15 letters each.  Bundles may consist of letters in which all 5 digits of zip code are the same (5-digit bundles), or they may consist of letters in which only the first 3 digits of zip code are the same (3-digit bundles).  If there are fewer than 10 letters to make up a bundle of either type, those letters are mailed first class.

You are to write a program to read a data set of 5-digit zip codes, one per line. Input ends when a negative number is entered. Your program should count the number of 5-digit bundles, 3-digit bundles, and first class letters.  

You should include as many letters as possible in 5-digit bundles first, then as many as possible in 3-digit bundles, with as few bundles of 10 to 15 letters as possible. For example, if there are 31 letters with the same zip code, they must be combined into exactly three 5-digit bundles.  

Not all zip codes in the data set will be valid.  A valid zip code consists of exactly 5 digits (0-9), all of which cannot be 0.  Non-numeric characters are not allowed.  At the end of your output, print the invalid zip codes found.  (Duplicates need only be printed once.)

Print a report that lists 5-digit zip code bundles first, with the number of letters and number of bundles for each zip code.  Next list all 3-digit zip code bundles with the same two counts, followed by all zip codes that are not bundled and to be sent first class.  

At the end print totals of letters and bundles, followed by the number of invalid zip codes and a list of these.  Single space the report, and print blank lines following the heading, before the total line, and between the three groups of zip codes.  For 3-digit bundles, print the zip codes in the form dddxx, where ddd represents the three significant digits and xx represents the last two digits to be omitted. Your output should be similar to that shown in the sample.

**Sample:**  
*SAMPLE INPUT:*   
95864  
95864  
95864  
95867  
95920  
9j876  
95616                   
95616                   
95747                   
95814                   
95818                   
95818                   
8976  
95818                   
95818                   
95819                   
95819                   
00000  
95819                   
95819                   
95819                   
95819                   
95819                   
95825  
95825  
95825  
95825  
95825  
95826  
95826  
95826  
95826  
95826  
95826  
95827  
8976  
95833  
95833  
95833  
95833  
95819                   
95819  
95819  
95819  
95833  
95833  
95833  
95864  
95864  
95864  
123456  
95864  
95864  
95864  
95864  
-1

*SAMPLE OUTPUT:*  

	ZIP         LETTERS     BUNDLES

	95819         11           1
	95864         10           1
	
	958xx         25           2
	
	95616          2           0
	95747          1		   0
	95920          1		   0

	TOTALS        50           4

	INVALID ZIP CODES
	9j876
	8976
	00000
	123456



Input Format:

Output Format:

Sample Input:

Sample Output:

Python 3 starter code:


Reference solution (C++):
	//This is sample code when data is read from an input file until end of file
    /*-----------------------------  bulk.c ----------------------------------

			Bulk Mail

	Read zip codes, validate and sort them, omitting bad codes.
	Combine into bundles of 10-20 letters each.  Create bundles
	by matching all 5 digits first, then by matching first 3
	digits.  Other letters go first class.  Print report by
	zip code with totals, and print invalid codes.
	*/
	#define DATA "bulk.dat"
	#define MAX 20
	#define MIN 10
	#include <stdio.h>
	#include <ctype.h>
	#include <string.h>
	#include <stdlib.h>
	#include<conio.h>

	void main(void)	
	{
	FILE *fp;
	long zip[500], zip2[500], num, comp, valid(char*);
	int letters, bundles, prev, count;
	int flag, i, j, k, n, savi, nzip, nerr;
	int printout(int, long, int), adderr(char[][12], char*, int);
	char s[12], err[20][12];

	if((fp = fopen(DATA, "r")) == NULL)
	    {  puts("Can't open file");  exit(0);  }
	letters = bundles = 0;
	i = n = 0;
	printf("ZIP         LETTERS     BUNDLES\n\n");
	while(fgets(s, 12, fp) != NULL)  {
	    s[strlen(s)-1] = '\0';
	    if((num = valid(s)) > 0)
		zip[i++] = num;
	    else
		n = adderr(err, s, n);
	}
	nzip = i;
	nerr = n;
	for(i = 0; i < nzip-1; ++i)      /* sort the zips */
	    for(j = 0; j < nzip-1; ++j)
		if(zip[j] > zip[j+1]) {
		    comp = zip[j];
		    zip[j] = zip[j+1];
		    zip[j+1] = comp;
		}
	i = prev = k = 0;
	flag = 0;		/* first pass for 5-digit codes */
	while(i < nzip)  {
	    count = 0;
	    comp = zip[i];
	    savi = i;
	    while(zip[i] == comp && i < nzip)
		i++;
	    count = i - savi;
	    if(count >= MIN)  {
		letters += count;
		bundles += printout(count, comp, flag);
		for(j = prev; j < savi; ++j)  /* copy nonbundled to zip2 */
		    zip2[k++] = zip[j];
		prev = i;
	    }
	}     /* end while i < nzip  */
	for(j = prev; j < nzip; ++j)
	    zip2[k++] = zip[j];		/* end first pass */
	putchar('\n');
	i = 0;
	nzip = k;
	flag = 1;			/* 2nd pass for 3-digit codes */
	while(i < nzip)  {
	    savi = i;
	    comp = zip2[i++] / 100;
	    while(zip2[i]/100 == comp && i < nzip)
		i++;
	    count = i - savi;
	    if(count >= MIN)  {
		letters += count;
		bundles += printout(count, comp, flag);
		for(j = savi; j < i; ++j)  /* zero out bundled positions */
		    zip2[j] = 0;
	    }
	}  /* end while i < nzip and end of 2nd pass */
	putchar('\n');
	i = 0;
	while(i < nzip)  {	/* last pass for first class zips */
	    while(zip2[i] == 0 && i < nzip)
		i++;
	    comp = zip2[i++];
	    count = 1;
	    while(zip2[i] == comp && i < nzip)  {
		count++;
		i++;
	    }
	    printf("%ld         %3d           0\n", comp, count);
	    letters += count;
	}   /* end while i < nzip and end last pass  */
	printf("\nTOTALS        %3d          %2d\n\n", letters, bundles);
	printf("INVALID ZIP CODES\n\n");
	for(n = 0; n < nerr; ++n)
	    printf("%s\n", err[n]);
	 getch();
	}
	long valid (char *s)
	{
	int i;

	if(strlen(s) != 5)
	    return -1;
	for(i = 0; isdigit(s[i]); ++i)
	    ;
	if(i != 5)                 /* if < 5 it's nonnumeric */
	    return -1;
	return atol(s);               /* convert to numeric */
	}
	int printout(int count, long comp, int flag)
	{
	int k, rem;		/* k is no. bundles */

	k = count / MAX;
	rem = count % MAX;
	if(rem > 0)
	    k++;
	if(flag == 1)
	    printf("%3ldxx", comp);
	else
	    printf("%5ld", comp);
	printf("         %3d          %2d\n", count, k);
	return k;
	}
	int adderr(char err[][12], char *s, int n)
	{
	int i;

	for(i = 0; i < n; ++i)
	    if(strcmp(err[i], s) == 0)
		return n;
	strcpy(err[n], s);
	/*	printf("%s   %s\n", err[n], s);   */
	return ++n;
	}