Created
May 31, 2018 20:27
-
-
Save Cxarli/aea94e5fc1e7df0fbf2f17b436fb6bdf to your computer and use it in GitHub Desktop.
My implementation of the pascal triangle
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdlib.h> | |
#include <stdio.h> | |
#include <stdint.h> | |
#include <string.h> | |
/// Get the n-th row of the pascal triangle | |
/// NOTE: This is a pure, recursive function without caching | |
/// n: the nth row to calculate | |
/// row: the pointer to an array with n+1 elements to store the row in | |
void pure_get_row(uint32_t n, uint32_t *row) { | |
if (n == 0) { | |
row[0] = 1; | |
return; | |
} | |
uint32_t *prev_row = malloc(n * sizeof(uint32_t)); | |
pure_get_row(n-1, prev_row); | |
row[0] = prev_row[0]; // == 1 | |
for (uint32_t i=1; i < n; i++) { | |
row[i] = prev_row[i - 1] + prev_row[i]; | |
} | |
row[n] = prev_row[n - 1]; // == 1 | |
} | |
/// Get the n-th row of the pascal triangle | |
/// NOTE: This is a non-pure, recursive function with caching | |
/// n: the nth row to get or calculate | |
/// cache: an array of arrays to retrieve or store the rows | NULL | |
/// row: the row to store the result in | NULL | |
/// NOTE: cache xor row has to be non-null | |
/// WARNING: Allocates memory for row if both cache and row are NULL; cache has precedence over row | |
uint32_t *get_row_with_cache(uint32_t n, uint32_t **cache, uint32_t *row) { | |
// Check if the row already exists in the cache | |
if (cache != NULL && cache[n][0] != (uint32_t) -1) { | |
return cache[n]; | |
} | |
// Get the location to store the row | |
if (cache != NULL) { | |
row = cache[n]; | |
} | |
else if (row == NULL) { | |
// Neither cache nor row given, so allocate some memory | |
row = malloc((n+1) * sizeof(uint32_t)); | |
} | |
// --- Calculate the row | |
// Hard-coding first row | |
if (n == 0) { | |
row[0] = 1; | |
return row; | |
} | |
uint32_t *prev_row = get_row_with_cache(n - 1, cache, NULL); | |
row[0] = prev_row[0]; | |
for (uint32_t i=1; i < n; i++) { | |
row[i] = prev_row[i - 1] + prev_row[i]; | |
} | |
row[n] = prev_row[n - 1]; | |
// Since there was no cache, `prev_row` has been allocated, so we should free it | |
if (cache == NULL) { | |
free(prev_row); | |
} | |
return row; | |
} | |
/// Wrapper around `get_row_with_cache` to use it without cache | |
void get_row_without_cache(uint32_t n, uint32_t *row) { | |
get_row_with_cache(n, NULL, row); | |
} | |
/// Initialise the `cache` variable with `size` so it can be used | |
void cache_init(uint32_t **cache, size_t size) { | |
for (size_t i=0; i < size; i++) { | |
cache[i] = malloc((i + 1) * sizeof(uint32_t)); | |
cache[i][0] = (uint32_t) -1; | |
} | |
} | |
/// Free all resources malloc'ed by `cache_init` | |
void cache_free(uint32_t **cache, size_t size) { | |
for (size_t i=0; i < size; i++) { | |
free(cache[i]); | |
} | |
} | |
/// If you need to read this description, you'd better go to sleep | |
#define print_n_spaces(n) printf("%*s", (n), "") | |
int main(void) { | |
#ifndef AMOUNT_ROWS | |
#define AMOUNT_ROWS 15 | |
#endif | |
// NOTE: Works best with odd numbers because there's a centre point | |
#define MAX_WIDTH_NUMBER 7 | |
// Make sure one and only one algorithm is chosen | |
#if (defined(CACHE) + defined(NO_CACHE) + defined(SEQ)) != 1 | |
#error "Specify CACHE, NO_CACHE xor SEQ" | |
// To mute future errors that'll arrive from this issue | |
uint32_t *row = NULL; | |
#endif | |
// Make sure SEQ+SINGLE is declared invalid | |
#if defined(SEQ) && defined(SINGLE) | |
#error "Are you sure you want to get a single value with the sequential algorithm?" | |
#endif | |
#ifdef CACHE | |
uint32_t *cache[AMOUNT_ROWS + 1]; | |
cache_init((uint32_t**) &cache, AMOUNT_ROWS + 1); | |
#endif | |
#ifdef SEQ | |
uint32_t *prev = malloc((AMOUNT_ROWS + 1) * sizeof(uint32_t)); | |
uint32_t *row = malloc((AMOUNT_ROWS + 1) * sizeof(uint32_t)); | |
row[0] = (uint32_t) -1; | |
prev[0] = (uint32_t) -1; | |
// Act as if we have a complete cache, while only giving two values | |
uint32_t *mini_cache[2]; | |
mini_cache[0] = prev; | |
mini_cache[1] = row; | |
#endif | |
#ifdef SINGLE | |
for (uint32_t n_row = AMOUNT_ROWS; n_row != 0; n_row = 0) { | |
#else | |
for (uint32_t n_row = 0; n_row <= AMOUNT_ROWS; n_row++) { | |
#endif | |
#ifdef CACHE | |
uint32_t *row = get_row_with_cache(n_row, (uint32_t**) &cache, NULL); | |
#endif | |
#ifdef NO_CACHE | |
uint32_t *row = malloc((n_row + 1) * sizeof(uint32_t)); | |
get_row_without_cache(n_row, row); | |
#endif | |
#ifdef SEQ | |
get_row_with_cache(n_row, mini_cache - n_row + 1, row); | |
#endif | |
#ifdef NO_PRINT | |
// Ignore warnings about unused row | |
(void) row; | |
#else | |
// Print the number of the row | |
printf("%02x", n_row); | |
// Print some padding so the pyramid aligns nicely | |
print_n_spaces((AMOUNT_ROWS - n_row) * (MAX_WIDTH_NUMBER / 2 + 1)); | |
// Print all digits from this row | |
for (uint32_t i=0; i <= n_row; i++) { | |
printf("%*u ", MAX_WIDTH_NUMBER, row[i]); | |
} | |
printf("\b\n"); | |
#endif | |
#ifdef SEQ | |
// Copy new row to previous | |
memcpy(prev, row, (AMOUNT_ROWS + 1) * sizeof(uint32_t)); | |
// Set new row as empty so it can be reused again | |
row[0] = (uint32_t) -1; | |
#endif | |
#ifdef NO_CACHE | |
free(row); | |
#endif | |
} | |
#ifdef CACHE | |
cache_free((uint32_t**) &cache, AMOUNT_ROWS + 1); | |
#endif | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment