mirror of
https://github.com/TheAlgorithms/C
synced 2024-11-25 14:59:36 +03:00
184 lines
4.5 KiB
C
184 lines
4.5 KiB
C
/**
|
|
* \file
|
|
* \brief [Problem 20](https://projecteuler.net/problem=20) solution
|
|
* \author [Krishna Vedala](https://github.com/kvedala)
|
|
*
|
|
* Implementation uses a custom `big_int` structure that can store arbitrarily
|
|
* large integer numbers.
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
|
|
/**
|
|
* store arbitratily large integer values
|
|
* as a linked list of digits.
|
|
*/
|
|
typedef struct _big_int
|
|
{
|
|
char value; /**< tens place (single digit) */
|
|
struct _big_int *next_digit; /**< hundreds place */
|
|
struct _big_int *prev_digit; /**< units place */
|
|
} big_int;
|
|
|
|
#ifdef DEBUG
|
|
/** print a digit from large integer */
|
|
void print_digit(const big_int *my_int)
|
|
{
|
|
printf("\tValue : %d\n\tNext : %p\n\tPrev : %p\n", my_int->value,
|
|
my_int->next_digit, my_int->prev_digit);
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* Function that allocates memory to add another
|
|
* digit at the MSB
|
|
*/
|
|
big_int *add_digit(big_int *digit, char value)
|
|
{
|
|
if (digit == NULL)
|
|
{
|
|
digit = (big_int *)malloc(sizeof(big_int));
|
|
if (!digit)
|
|
{
|
|
perror("Unable to allocate memory!");
|
|
return NULL;
|
|
}
|
|
digit->value = value;
|
|
digit->next_digit = NULL;
|
|
digit->prev_digit = NULL;
|
|
|
|
return digit;
|
|
}
|
|
|
|
if (digit->next_digit)
|
|
{
|
|
digit->next_digit->value = value;
|
|
return digit->next_digit;
|
|
}
|
|
|
|
digit->next_digit = (big_int *)malloc(sizeof(big_int));
|
|
if (digit->next_digit == NULL)
|
|
{
|
|
perror("Unable to allocate memory!");
|
|
return NULL;
|
|
}
|
|
digit->next_digit->value = value;
|
|
digit->next_digit->next_digit = NULL;
|
|
digit->next_digit->prev_digit = digit;
|
|
return digit->next_digit;
|
|
}
|
|
|
|
/**
|
|
* Function to remove digits preceeding the
|
|
* current digit.
|
|
*/
|
|
char remove_digits(big_int *digit, int N)
|
|
{
|
|
if (digit == NULL)
|
|
return 0;
|
|
|
|
if (digit->next_digit == NULL)
|
|
{
|
|
free(digit);
|
|
digit = NULL;
|
|
return 0;
|
|
}
|
|
|
|
if (N > 0)
|
|
return remove_digits(digit->next_digit, N - 1);
|
|
|
|
return remove_digits(digit->next_digit, 0);
|
|
}
|
|
|
|
/** Main function */
|
|
int main(int argc, char **argv)
|
|
{
|
|
unsigned int N = 5;
|
|
big_int *ptr = add_digit(NULL, 1); /* start with 1 */
|
|
const big_int *ptr0 = ptr; /* save the first location */
|
|
unsigned long sum_digits = 0;
|
|
unsigned long num_digits = 0;
|
|
|
|
if (argc == 2)
|
|
N = atoi(argv[1]);
|
|
|
|
clock_t start_time = clock();
|
|
|
|
for (unsigned int i = 1; i <= N; i++)
|
|
{
|
|
int carry = 0;
|
|
#ifdef DEBUG
|
|
printf("%3d: ", i);
|
|
#endif
|
|
ptr = (big_int *)ptr0; /* multiply every digit with i */
|
|
while (ptr)
|
|
{
|
|
#ifdef DEBUG
|
|
printf("%p\t", ptr);
|
|
#endif
|
|
unsigned int tmp = ptr->value * i + carry;
|
|
if (tmp >= 10)
|
|
{
|
|
div_t tmp2 = div(tmp, 10);
|
|
carry = tmp2.quot;
|
|
tmp = tmp2.rem;
|
|
}
|
|
else
|
|
carry = 0;
|
|
|
|
if (carry > 0 && ptr->next_digit == NULL)
|
|
add_digit(ptr, 0);
|
|
|
|
ptr->value = tmp;
|
|
|
|
if (i == N)
|
|
/*
|
|
* sum digits on the last iteration
|
|
* this avoid having another loop over all digits
|
|
*/
|
|
sum_digits += tmp;
|
|
|
|
if (ptr->next_digit)
|
|
/* more digits available */
|
|
ptr = ptr->next_digit;
|
|
else
|
|
/* no more digits left - reached MSB */
|
|
break;
|
|
}
|
|
#ifdef DEBUG
|
|
printf("\n");
|
|
#endif
|
|
}
|
|
|
|
clock_t end_time = clock();
|
|
|
|
#ifdef DEBUG
|
|
printf("ptr = %p\n", ptr);
|
|
printf("%d! = ", N);
|
|
#endif
|
|
|
|
/* Notice that in the loop above, we make sure that at the end of the loop,
|
|
* ptr is pointing to the last digit. Thus we can avoid using another loop.
|
|
*/
|
|
// ptr = &my_int;
|
|
// /* move ptr to the MSB digit */
|
|
// while (ptr->next_digit)
|
|
// ptr = ptr->next_digit;
|
|
do
|
|
{
|
|
putchar(ptr->value + 0x30); /* convert digit to ASCII char */
|
|
ptr = ptr->prev_digit;
|
|
num_digits++;
|
|
} while (ptr); /* after coming to units place, there will be no valid ptr */
|
|
|
|
printf("\nTime taken: %.4g millisecond\n",
|
|
1e3 * (end_time - start_time) / CLOCKS_PER_SEC);
|
|
printf(
|
|
"Digit Sum = %lu\tNumber of digits = %lu\tStorage space = %.3gkb\t \n",
|
|
sum_digits, num_digits, num_digits * sizeof(big_int) / 1024.0);
|
|
|
|
remove_digits((big_int *)ptr0, -1);
|
|
return 0;
|
|
}
|