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


# define RAID5_ALGORITHM_LEFT_ASYMMETRIC 	(0)
# define RAID5_ALGORITHM_RIGHT_ASYMMETRIC 	(1)
# define RAID5_ALGORITHM_LEFT_SYMMETRIC 	(2)
# define RAID5_ALGORITHM_RIGHT_SYMMETRIC 	(3)

char *progname = "sectors";

static unsigned long
raid5_compute_block (unsigned long r_sector, 
	unsigned int raid_disks,
	unsigned int data_disks, 
	unsigned int *dd_idx,
	unsigned int *pd_idx, int blocks_per_chunk);

main ()
{
	unsigned long block;
	int dd_idx, pd_idx;
	int blocks_per_chunk;

	for (block = 0; block < 32; block++) {
		raid5_compute_block (block, 3, 2, &dd_idx, &pd_idx, 1);
		raid5_compute_block (block, 4, 3, &dd_idx, &pd_idx, 1);
		putchar ('\n');
	}

#if 0
	for (blocks_per_chunk = 1; 
	     blocks_per_chunk <= 512; 
	     blocks_per_chunk <<= 1) {
		raid5_compute_block (5002, 3, 2, &dd_idx, &pd_idx, 
		    blocks_per_chunk);
		raid5_compute_block (5011, 3, 2, &dd_idx, &pd_idx, 
		    blocks_per_chunk);
	}

	for (block = 5000; block < 5012; block++)
		raid5_compute_block (block, 3, 2, &dd_idx, &pd_idx, 32);

	for (block = 5000; block < 5012; block++)
		raid5_compute_block (block, 3, 2, &dd_idx, &pd_idx, 64);

	for (block = 5000; block < 5012; block++)
		raid5_compute_block (block, 3, 2, &dd_idx, &pd_idx, 128);
#endif
	
	exit (0);
}

static unsigned long
raid5_compute_block (unsigned long r_sector, 
	unsigned int raid_disks,
	unsigned int data_disks, 
	unsigned int *dd_idx,
	unsigned int *pd_idx, int blocks_per_chunk)
{
	unsigned long stripe;
	unsigned long chunk_number;
	unsigned int chunk_offset;
	unsigned long new_sector;
	int algorithm = RAID5_ALGORITHM_LEFT_SYMMETRIC;

# define TESTING
# ifdef TESTING
# define PU(V)	printf ("%s = %6lu ", #V, V)
# else
# define PU(V)
# endif /* TESTING */

	PU (r_sector);

	/* First compute the information on this sector */

	/*
	 * Compute the chunk number and the sector offset inside the chunk
	 */
	chunk_number = r_sector / blocks_per_chunk;
	chunk_offset = r_sector % blocks_per_chunk;

#if 0
	PU (chunk_number);
	PU (chunk_offset);
#endif

	/*
	 * Compute the stripe number
	 */
	stripe = chunk_number / data_disks;

#if 0
	PU (stripe);
#endif

	/*
	 * Compute the data disk and parity disk indexes inside the stripe
	 */
	*dd_idx = chunk_number % data_disks;

	/*
	 * Select the parity disk based on the user selected algorithm.
	 */
	switch (algorithm) {
	case RAID5_ALGORITHM_LEFT_ASYMMETRIC:
		*pd_idx = data_disks - stripe % raid_disks;
		if (*dd_idx >= *pd_idx)
			(*dd_idx)++;
		break;
	case RAID5_ALGORITHM_RIGHT_ASYMMETRIC:
		*pd_idx = stripe % raid_disks;
		if (*dd_idx >= *pd_idx)
			(*dd_idx)++;
		break;
	case RAID5_ALGORITHM_LEFT_SYMMETRIC:
		*pd_idx = data_disks - stripe % raid_disks;
		*dd_idx = (*pd_idx + 1 + *dd_idx) % raid_disks;
		break;
	case RAID5_ALGORITHM_RIGHT_SYMMETRIC:
		*pd_idx = stripe % raid_disks;
		*dd_idx = (*pd_idx + 1 + *dd_idx) % raid_disks;
		break;
	default:
		fprintf (stderr, "raid5: unsupported algorithm %d\n", 
		    algorithm);
	}

	PU (*dd_idx);
#if 0
	PU (*pd_idx);
#endif

	/*
	 * Finally, compute the new sector number
	 */

	new_sector = stripe * blocks_per_chunk + chunk_offset;

	PU (new_sector);

	putchar ('\n');

	return new_sector;
}
