I have a array of strings and I want to find the first pseudopalindrome in the array for every string (if there is any). So I decided to sort my array at first, then reverse the word and do a binary search for a reversed word. So here it is what I have so far:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char len(char *x){
char len = 0;
while (*x != '\0'){
x++;
len++;
}
return len;
}
char compare(char *x, char *y){
char x0 = &x;
char y0 = &y;
while (*x != '\0'){
if (tolower(*x) < tolower(*y)) return -1;
if (tolower(*x) > tolower(*y)) return 1;
x++;
y++;
}
// if we are here it means that strings are equal (case insensitive)
x = &x0;
y = &y0;
while (*x != '\0'){
if (*x > *y) return -1;
if (*x < *y) return 1;
x++;
y++;
}
// strings are equal (case sensitive)
return 0;
}
char *reverse(char *x){
int i, j;
char temp, *rev = NULL;
rev = malloc(sizeof(char)*(len(x)+1));
rev = strcpy(rev,x);
i = 0;
j = len(x) - 1;
while (i < j){
temp = rev[i];
rev[i] = x[j];
rev[j] = temp;
i++;
j--;
}
return rev;
}
int binsearch(char *x, char *A, int len){
int l, r, m, index;
l = 0;
r = len - 1;
index = -1;
while (l <= r){
m = (l + r) / 2;
if (compare(x, A[m]) == 0){
index = m;
r = m - 1;
}
else if (compare(x, A[m]) == -1) r = m - 1;
else l = m + 1;
}
return index;
}
int main()
{
int n, i, j, k, fnd;
char T[10000][101], temp[101];
scanf("%d", &n);
for (i = 0; i < n; i++){
scanf("%s", &T[i]);
}
for (i = 1; i < n; i++){
strcpy(temp, T[i]);
j = i - 1;
while (j >= 0 && compare(T[j], temp) == 1){
strcpy(T[j+1], T[j]);
j--;
}
strcpy(T[j+1], temp);
}
for (i = 0; i < n; i++){
fnd = binsearch(reverse(T[i]), T, n);
printf("%d", fnd);
}
return 0;
}
This program stops executing. The problem is probably with binary search as every function before executes well. But what's wrong with this binary search? Or what else can break the code?
Does the return type causing the problem .. for binary search no return type is mentioned
Edit 1. But you have not mentioned return type for function in its declaration
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
char len(char *x){
char len = 0;
while (*x != '\0'){
x++;
len++;
}
return len;
}
char compare(char *x, char *y){
char* x0 = x;
char* y0 = y;
while (*x != '\0'){
int a0 = tolower(*x);
int b0 = tolower(*y);
if ( a0 < b0)
return -1;
if ( a0 > b0)
return 1;
x++;
y++;
}
// if we are here it means that strings are equal (case insensitive)
x = x0;
y = y0;
while (*x != '\0'){
if (*x > *y) return -1;
if (*x < *y) return 1;
x++;
y++;
}
// strings are equal (case sensitive)
return 0;
}
char *reverse(char *x){
int i, j;
char temp, *rev = NULL;
rev = malloc(sizeof(char)*(len(x)+1));
rev = strcpy(rev,x);
i = 0;
j = len(x) - 1;
while (i < j){
temp = rev[i];
rev[i] = x[j];
rev[j] = temp;
i++;
j--;
}
return rev;
}
int binarysearch(char *x,char A[][101], int len){
int l, r, m, index;
l = 0;
r = len - 1;
index = -1;
while (l <= r){
m = (l + r) / 2;
if (compare(x, A[m]) == 0){
index = m;
r = m - 1;
}
else if (compare(x, A[m]) == -1) r = m - 1;
else l = m + 1;
}
return index;
}
int main()
{
int n, i, j, k, fnd;
char T[10000][101], temp[101];
scanf("%d", &n);
for (i = 0; i < n; i++){
scanf("%s", &T[i]);
}
for (i = 1; i < n; i++){
strcpy(temp, T[i]);
j = i - 1;
while (j >= 0 && compare(T[j], temp) == 1){
strcpy(T[j+1], T[j]);
j--;
}
strcpy(T[j+1], temp);
}
for (i = 0; i < n; i++){
fnd = binarysearch(reverse(T[i]), T, n);
printf("%d", fnd);
}
return 0;
}
Not really an answer, but code in comments is painful to read.
You may also try to simplify your code; it's quite astonishing to have some much code for just a palindrome; some functions you are reimplementing are part of standard c (strlen, strcasecmp, strdup)
The function for telling if a word is palindrome is expected to really simple; here a sample of what it could be
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
bool isspeudopalindrom(const char *x){
for (int i = 0; i < strlen(x) / 2; ++i) {
if (tolower(x[i]) != tolower(x[strlen(x) - 1 - i]))
return false;
}
return true;
}
int main(int argc, char *argv[])
{
for (int i = 1; i < argc; i++){
if (isspeudopalindrom(argv[i]))
printf("palindrom\n");
else
printf("not palindrom\n");
}
return 0;
}
So this program crashes and tells me "Aborted (core dumped)" but only when my decleration of "GENERATIONS" is greater than 6... I know its a pain that I've uploaded the whole code but I really cant figure out where it is other than it's after the return from "fibonacci_quasicrystal_generator(GENERATIONS, crystal);", as the printf statement just after gets printed, then the message appears. Code below:
#define GENERATIONS 5
#define OUTFILE "frequencies.txt"
#define GNUPLOT_EXE "gnuplot"
#define GNUPLOT_SCRIPT "frequencyplot.script"
static void fibonacci_quasicrystal_generator(int generations, char * chain);
static int plot();
int main()
{
double k = 1.0, m_a = 100.0, m_b = 1.0, m = 0.0;
char * crystal = malloc(2);
//strcopy(crystal, "A"); //gsl_vector * y_vector = gsl_vector_calloc(CHAIN_LENGTH);
fibonacci_quasicrystal_generator(GENERATIONS, crystal);
if (crystal == NULL){
printf("Crystal write failed.");
exit(0);
}
int chain_length = strlen(crystal);
printf("%i member Crystal generated, after %i generations.\n", chain_length, GENERATIONS);
gsl_matrix * a_matrix = gsl_matrix_calloc(chain_length, chain_length);
gsl_matrix * b_matrix = gsl_matrix_calloc(chain_length, chain_length);
gsl_matrix_set_identity(b_matrix);
gsl_vector * eigenvalues_vector = gsl_vector_calloc(chain_length);
for (int i = 0; i < chain_length; ++i){
if (crystal[i] == 'A'){
m = m_a;
} else {
m = m_b;
}
for (int j = 0; j < chain_length; ++j){
if ((i == j) && (i != 0 && i != chain_length)){
gsl_matrix_set(a_matrix, i, j,(2*k)/m);
}
else if (i == j-1){
gsl_matrix_set(a_matrix, i, j,(-1)*(k/m));
}
else if (i == j+1){
gsl_matrix_set(a_matrix, i ,j, (-1)*(k/m));
}
}
}
gsl_eigen_gensymm_workspace * workspace = gsl_eigen_gensymm_alloc(chain_length);
gsl_eigen_gensymm(a_matrix, b_matrix, eigenvalues_vector, workspace);
gsl_eigen_gensymm_free(workspace);
free(crystal);
gsl_matrix_free(a_matrix);
gsl_matrix_free(b_matrix);
gsl_sort_vector(eigenvalues_vector);
FILE * outfile = fopen(OUTFILE, "w");
for (int i = 0; i < chain_length; ++i){
fprintf(outfile, "%e \t%i \r\n", pow(gsl_vector_get(eigenvalues_vector, i),2), i);
}
fclose(outfile);
gsl_vector_free(eigenvalues_vector);
plot();
return 0;
}
static void fibonacci_quasicrystal_generator(int generations, char * chain){
printf("generating fibonacci quasicrystal...\n");
int i;
i = 0;
char * chain_1 = malloc(2), * chain_2 = malloc(2), * tmp = malloc(2);
strcpy(chain_1, "B");
strcpy(chain_2, "A");
size_t chain_1_size = strlen(chain_1) + 1, chain_2_size = strlen(chain_2) + 1;
if (generations == 1){
chain = realloc(chain, chain_1_size);
snprintf(chain, chain_1_size, "%s", chain_1);
}
else if (generations == 2){
chain = realloc(chain, chain_2_size);
snprintf(chain, chain_2_size, "%s", chain_2);
}
else if (generations > 2){
size_t chain_3_size = strlen(chain_1) + strlen(chain_2) + 1;
char * chain_3 = malloc(chain_3_size);
printf("%i\n", generations);
for (i = 0; i < generations - 1; ++i){
printf("%i\n", i);
snprintf(chain_3, chain_3_size, "%s%s", chain_1, chain_2);
chain_1_size = chain_2_size;
chain_2_size = chain_3_size;
if ((tmp = realloc(chain_1, chain_1_size)) != NULL){
chain_1 = tmp;
}
if ((tmp = realloc(chain_2, chain_2_size)) != NULL){
chain_2 = tmp;
}
snprintf(chain_1, chain_1_size, "%s", chain_2);
snprintf(chain_2, chain_2_size, "%s", chain_3);
if (i < generations - 2){
chain_3_size = strlen(chain_1) + strlen(chain_2) + 1;
if ((tmp = realloc(chain_3, chain_3_size)) != NULL){
chain_3 = tmp;
} else {
printf("oops!\n");
exit(1);
}
}
}
chain = realloc(chain, chain_3_size);
snprintf(chain, chain_3_size, "%s", chain_3);
free(chain_3);
}
free(chain_1);
free(chain_2);
}
static int plot(){
char command[PATH_MAX];
snprintf(command, sizeof(command), "%s %s", GNUPLOT_EXE, GNUPLOT_SCRIPT);
system(command);
return 0;
}
The problem is that char *chain into fibonacci_quasicrystal_generator function has local scope: the function does not modify the crystal pointer of main, so that pointer is left with 2 bytes.
You can change the function to
static char *fibonacci_quasicrystal_generator(int generations, char * chain)
{
// YOUR STUFF
return chain;
}
And call it from main using
crystal = fibonacci_quasicrystal_generator(GENERATIONS, crystal);
You can achieve the same using a double pointer so
static void ibonacci_quasicrystal_generator(int generations, char ** chain)
See Latest Update
Given the following function, note the location of the call free(tmp):
int *power_arr(int *n, int nlength, int exp, int *res_length)
{
int *tmp, *rt, *bufp;
int bufp_length, i, dbg_i;
rt = malloc(sizeof(int) * 1000);
bufp = malloc(sizeof(int) * 1000);
if (!rt || !bufp)
{
return NULL;
}
copy(rt, n, nlength);
copy(bufp, n, nlength);
*res_length = bufp_length = nlength;
while (--exp > 0)
{
for (i = *n - 1; i > 0; i--)
{
tmp = sum(rt, *res_length, bufp, bufp_length, res_length);
if (!tmp)
{
exit(-1);
}
copy(rt, tmp, *res_length);
//free(tmp); // produces undefined output?
}
copy(bufp, rt, *res_length);
bufp_length = *res_length;
}
free(tmp);
free(bufp);
return rt;
}
The result of the following main function:
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
int b[] = { 3 };
int r, i;
int *rlength, *res;
r = 0;
rlength = &r;
res = power_arr(b, 1, 3, rlength);
printf("Length = %d\n", *rlength);
for (i = 0; i < *rlength; i++)
{
printf("i=");
printf("%d\n", res[i]);
}
printf("\n");
exit(0);
}
Is:
Length = 2
i=2
i=7
My understanding of the first scenario, is that there is a memory leak for every subsequent tmp = sum(rt, *res_length, bufp, bufp_length, res_length); call. Which point, I decided to move the call to free(tmp) inside the for loop. Once moved, I had noticed that the output had changed, as follows:
Length = 4
i=1018670
i=4
i=2
i=7
I can see that the answer begins at i[3]. Why does the movement of the free(tmp) call, cause this effect?
My understanding is that tmp becomes a dangling pointer after the free() call. Then, it is re-assigned a value returned by the function sum() - which was retrieved by a call to malloc(). Which point, a memory leak would occur by placing the call to free() in its original location. As the value of tmp will change, only freeing the last pointer assigned to it.
Edit:
Below is the code for the supporting functions.
int *pad(int *n, int nlength, int new_length, enum SIDE side)
{
int i, j;
int *padded;
if (nlength < 1 || new_length <= nlength)
{
return NULL;
}
padded = calloc(new_length, sizeof(int));
if (!padded)
{
return NULL;
}
if (side == LOW)
{
j = new_length - 1;
for (i = (nlength - 1); i >= 0; i--)
{
padded[j--] = n[i];
}
}
else
{
j = 0;
for (i = 0; i < nlength; i++)
{
padded[j++] = n[i];
}
}
return padded;
}
int *trim(int *n, int nlength, int *res_length)
{
int i, j;
int *res;
for (i = 0; i < nlength; i++)
{
if (n[i] > 0)
{
break;
}
}
*res_length = (nlength - i);
res = malloc(sizeof(int) * (*res_length));
if (!res)
{
return NULL;
}
j = 0;
while (i < nlength)
{
res[j++] = n[i++];
}
return res;
}
int *sum(int *n, int nlength, int *m, int mlength, int *sum_length)
{
int i, tmp, carry;
int *result, *trimmed, *op1, *op2;
enum SIDE side = LOW;
if (nlength == mlength)
{
op1 = n;
op2 = m;
}
else if (nlength > mlength)
{
op1 = n;
op2 = pad(m, mlength, nlength, side);
}
else
{
op1 = m;
op2 = pad(n, nlength, mlength, side);
}
result = malloc(sizeof(int) * (MAX(nlength, mlength) + 1));
if (!op1 || !op2 || !result)
{
return NULL;
}
carry = 0;
for (i = (MAX(nlength, mlength)) - 1; i >= 0; i--)
{
tmp = op1[i] + op2[i] + carry;
if (carry > 0)
{
carry = 0;
}
if (tmp >= 10)
{
carry = tmp / 10;
tmp = tmp % 10;
}
result[i + 1] = tmp;
}
if (carry > 0)
{
result[0] = carry--;
}
*sum_length = (MAX(nlength, mlength)) + 1;
trimmed = trim(result, *sum_length, sum_length);
free(result);
if (!trimmed)
{
return NULL;
}
return trimmed;
}
void copy(int *to, int *from, int length)
{
int i;
for (i = 0; i < length; i++)
{
to[i] = from[i];
}
}
Update:
After implementing the changes suggested in the first post, a double free error has begun occurring, to debug, I added the following print statements to the power_arr(). The output following shows that tmp is being assigned the same value from sum(), as it received on the initial call. Why?
Updated code showing debug printf statements:
for (i = *n - 1; i > 0; i--)
{
tmp = sum(rt, *res_length, bufp, bufp_length, res_length);
printf("first tmp = %d\n", tmp);
if (!tmp)
{
printf("tmp was null\n");
exit(-1);
}
copy(rt, tmp, *res_length);
printf("second tmp = %d\n", tmp);\
if (tmp != NULL)
{
printf("freeing tmp\n");
free(tmp);
tmp = NULL;
}
printf("tmp = %d\n", tmp);
}
The output:
first tmp = 11227072
second tmp = 11227072
freeing tmp
tmp = 0
first tmp = 11227072 <-- Why has the pointer value not changed?
second tmp = 11227072
freeing tmp <-- Double free now occuring.
*** Error in `./a.out': double free or corruption (fasttop): 0x0000000000ab4fc0 ***
Aborted
Update:
I believe I have tracked the bug down to the trim() function. I have posted the function with a loop to execute it 10 times in succession. As you can see in the output, trim() - which calls malloc(), returns the same pointer value on subsequent calls. Yet, each successive call to free does not trigger a double free error. Why is this the case?
int main()
{
int i, j, length;
int n[] = { 4, 5, 6 };
int m[] = { 0, 3, 5 };
int *num;
int *trimmed, *trimmed_length;
trimmed_length = &length;
for (i = 0; i < 10; i++)
{
num = (i % 2 == 0) ? n : m;
trimmed = trim(num, 3, trimmed_length);
if (!trimmed)
{
printf("trimmed was null\n");
exit(-1);
}
for (j = 0; j < *trimmed_length; j++)
{
printf("%d", trimmed[j]);
}
printf("\n");
free(trimmed);
}
exit(0);
}
int *trim(int *n, int nlength, int *res_length)
{
int i, j;
int *res;
for (i = 0; i < nlength; i++)
{
if (n[i] > 0)
{
break;
}
}
*res_length = (nlength - i);
res = malloc(sizeof(int) * (*res_length));
if (!res)
{
return NULL;
}
j = 0;
while (i < nlength)
{
res[j++] = n[i++];
}
printf("Returned pointer from trim() %d\n", res);
return res;
}
Output:
Returned pointer from trim() 39534608
456
Returned pointer from trim() 39534608
35
Returned pointer from trim() 39534608
456
Returned pointer from trim() 39534608
35
Returned pointer from trim() 39534608
456
Returned pointer from trim() 39534608
35
Returned pointer from trim() 39534608
456
Returned pointer from trim() 39534608
35
Returned pointer from trim() 39534608
456
Returned pointer from trim() 39534608
35
This also appears to be the behavior in my original question - which triggers a double free error. Why is the double free error not encountered in this particular situation?
You have a number of memory leak points and a full bug which is why the free fails [due to double free of the same pointer].
NOTE: I've done an update to this answer, but it's too big to fit here, so I've posted it as a second answer
I've joined all files into one so I could compile it [please pardon the gratuitous style cleanup], fixed the bug, and annotated all the hot spots [this compiles, but I didn't test it]:
#include <stdlib.h>
#include <stdio.h>
enum SIDE {
LOW
};
#define MAX(_x,_y) (((_x) > (_y)) ? (_x) : (_y))
// perform free if pointer is non-null -- set to null afterwards to prevent
// "double free"
#define FREEME(_ptr) \
do { \
if (_ptr != NULL) \
free(_ptr); \
_ptr = NULL; \
} while (0)
int *
pad(int *n, int nlength, int new_length, enum SIDE side)
{
int i,
j;
int *padded;
if (nlength < 1 || new_length <= nlength) {
return NULL;
}
padded = calloc(new_length, sizeof(int));
if (!padded) {
return NULL;
}
if (side == LOW) {
j = new_length - 1;
for (i = (nlength - 1); i >= 0; i--) {
padded[j--] = n[i];
}
}
else {
j = 0;
for (i = 0; i < nlength; i++) {
padded[j++] = n[i];
}
}
return padded;
}
int *
trim(int *n, int nlength, int *res_length)
{
int i,
j;
int *res;
for (i = 0; i < nlength; i++) {
if (n[i] > 0) {
break;
}
}
*res_length = (nlength - i);
res = malloc(sizeof(int) * (*res_length));
if (!res) {
return NULL;
}
j = 0;
while (i < nlength) {
res[j++] = n[i++];
}
return res;
}
int *
sum(int *n, int nlength, int *m, int mlength, int *sum_length)
{
int i,
tmp,
carry;
int *result,
*trimmed,
*op1,
*op2;
int padflg;
enum SIDE side = LOW;
// NOTE: this helps us remember whether to free op2 or not
padflg = 1;
// NOTE: here op2 comes from _caller_ -- so do _not_ free it in this
// function -- _this_ is the cause of the bug
// case (1)
if (nlength == mlength) {
op1 = n;
op2 = m;
padflg = 0;
}
// NOTE: here op2 comes from _pad_ -- so we do _want_ to free it so it
// doesn't leak
// case (2)
else if (nlength > mlength) {
op1 = n;
op2 = pad(m, mlength, nlength, side);
}
// case (3)
else {
op1 = m;
op2 = pad(n, nlength, mlength, side);
}
result = malloc(sizeof(int) * (MAX(nlength, mlength) + 1));
if (!op1 || !op2 || !result) {
if (padflg)
FREEME(op2);
FREEME(result);
return NULL;
}
carry = 0;
for (i = (MAX(nlength, mlength)) - 1; i >= 0; i--) {
tmp = op1[i] + op2[i] + carry;
if (carry > 0) {
carry = 0;
}
if (tmp >= 10) {
carry = tmp / 10;
tmp = tmp % 10;
}
result[i + 1] = tmp;
}
// NOTE: we want to free op2 for case (2)/(3) but we didn't remember
// how we got it: (1) means no free, (2)/(3) means free
// only free if this if we called pad, and _not_ if this pointer belongs
// to caller
if (padflg)
FREEME(op2);
if (carry > 0) {
result[0] = carry--;
}
*sum_length = (MAX(nlength, mlength)) + 1;
trimmed = trim(result, *sum_length, sum_length);
free(result);
return trimmed;
}
void
copy(int *to, int *from, int length)
{
int i;
for (i = 0; i < length; i++) {
to[i] = from[i];
}
}
int *
power_arr(int *n, int nlength, int exp, int *res_length)
{
int *tmp,
*rt,
*bufp;
int bufp_length,
i;
// NOTE: rt/bufp are memory leaks -- they are never freed
rt = malloc(sizeof(int) * 1000);
bufp = malloc(sizeof(int) * 1000);
// NOTE: this is a memory leak -- if one is null, but the other is non-null,
// you must free the non-null one or it leaks
if (!rt || !bufp) {
FREEME(rt);
FREEME(bufp);
return NULL;
}
copy(rt, n, nlength);
copy(bufp, n, nlength);
*res_length = bufp_length = nlength;
while (--exp > 0) {
for (i = *n - 1; i > 0; i--) {
tmp = sum(rt, *res_length, bufp, bufp_length, res_length);
if (!tmp) {
exit(-1);
}
copy(rt, tmp, *res_length);
// NOTE: this will now work because of the padflg changes in
// sum
#if 0
// free(tmp); // produces undefined output?
#else
FREEME(tmp);
#endif
}
copy(bufp, rt, *res_length);
bufp_length = *res_length;
}
FREEME(bufp);
return rt;
}
int
main(void)
{
int b[] = { 3 };
int r,
i;
int *rlength,
*res;
r = 0;
rlength = &r;
res = power_arr(b, 1, 3, rlength);
printf("Length = %d\n", *rlength);
for (i = 0; i < *rlength; i++) {
printf("i=");
printf("%d\n", res[i]);
}
printf("\n");
exit(0);
}
UPDATE: I'm adding a second answer here because the new code example is too big to fit as an update to my previous one.
I found a few more issues. If you run the original test program repeatedly, it gives different [incorrect] answers on each run. That is, loop on it from a script.
I wasn't able to fix the remaining bug(s) with the existing functions and arguments. The sheer complexity was adding to the problems.
Once I realized what you were really trying to do (e.g. multiprecision math), I was able to apply several simplifications:
Instead of passing around int *vals, int *len everywhere, I created a "big number" struct:
- contains pointer to data and length
- length of data buffer can grow/shrink dynamically
- has reference count
You were doing your math in "big endian" mode. This made it necessary to reallocate frequently (e.g. as in pad and copy). This also added to the complexity. I switched this to use "little endian", which is much easier to work with. And, it's also a bit [considerably] faster.
I also noticed in your power function that you were using sum to do what was essentially rt += bufp;. So, I created a version sumeq that operated on power's rt value directly, which further simplified things.
Note: You can still do it big endian, but most packages I've seen use little. Still, the base routines to create/grow the struct are endian agnostic, and you could create big endian versions of the little endian functions I created [which came from your big endian functions]
Here's the actual code. It is buildable and runnable. It will test all versions, including your original code [with the issues].
// badfree -- test program
#define TSTDEFALL
// badfree/badfree.h -- badfree control
#ifndef _badfree_badfree_h_
#define _badfree_badfree_h_
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#ifdef DEBUG
#define dbgprtf(_fmt...) printf(_fmt)
#define dbgexec(_exec) _exec
#else
#define dbgprtf(_fmt...) /**/
#define dbgexec(_exec) /**/
#endif
#define dbgleshow(_num,_who) dbgexec(leshow(_num,0,_who))
#define sysfault(_fmt...) \
do { \
printf(_fmt); \
fflush(stdout); \
_sysfault(); \
} while (0)
typedef unsigned int u32;
#if BIGDIG == 1
typedef char bigdig_t;
#elif BIGDIG == 2
typedef short bigdig_t;
#else
typedef int bigdig_t;
#endif
typedef bigdig_t *bigdig_p;
typedef const bigdig_t *bigdig_pc;
// multiprecision number control
struct bignum {
u32 num_opts; // options
const char *num_tag; // symbol name
int num_refcnt; // reference count
int num_curlen; // current length
int num_maxlen; // maximum length
bigdig_p num_base; // pointer to vector
};
typedef struct bignum bgn_t;
typedef bgn_t *bgn_p;
typedef const bgn_t *bgn_pc;
// num_opts definitions
#define BGNASTRUCT (1 << 0) // struct was allocated
#define BGNABASE (1 << 1) // num_base was allocated
#define BGNINITAV (1 << 2) // bgninit -- init values
#define BGNINITDUP (1 << 3) // bgninit -- init values
#define BGNTRIM (1 << 4) // trim number
#define BGNRAW (1 << 5) // output number in raw order
#define BGNACQUIRE(_num) \
bgnacquire(_num)
#define BGNRELEASE(_num) \
_num = bgnrelease(_num)
#define BGNINIT(_sym,_opt,_len...) \
_sym = bgninit(#_sym,_opt,_len)
#define BGNASSERT(_num,_opt) \
do { \
if (((_num)->num_opts & _opt) == (_opt)) \
break; \
sysfault("BGNASSERT: failure -- num=%p num_opts=%8.8X opt=%8.8X\n", \
num,num->num_opts,_opt); \
} while (0)
#define BGNASSERT_NOT(_num,_opt) \
do { \
if (((_num)->num_opts & _opt) != (_opt)) \
break; \
sysfault("BGNASSERT_NOT: failure -- num=%p num_opts=%8.8X opt=%8.8X\n", \
num,num->num_opts,_opt); \
} while (0)
enum SIDE {
LOW
};
#define MAX(_x,_y) (((_x) > (_y)) ? (_x) : (_y))
#define MIN(_x,_y) (((_x) < (_y)) ? (_x) : (_y))
// perform free if pointer is non-null -- set to null afterwards to prevent
// "double free" and use of pointer after it has been freed
#define FREEME(_ptr) \
do { \
if (_ptr != NULL) \
free(_ptr); \
_ptr = NULL; \
} while (0)
// test control
struct tstctl {
int tst_allow; // allow test to be performed
const char *tst_tag; // name of test
void (*tst_fnc)(void); // test function
};
typedef struct tstctl tstctl_t;
typedef tstctl_t *tstctl_p;
typedef const tstctl_t *tstctl_pc;
#define FORTSTALL(_tst) \
_tst = tstlist; _tst->tst_tag != NULL; ++_tst
int
main(int argc, char **argv);
void
dotests(void);
void
dotest(tstctl_p tst);
void
usage(void);
void
becopy(bigdig_t *to, const bigdig_t *from, int length);
bigdig_t *
bepad(const bigdig_t *n, int nlength, int new_length, enum SIDE side);
bigdig_t *
betrim(const bigdig_t *n, int *res_length);
bigdig_t *
besum(bigdig_t *n, int nlength, bigdig_t *m, int mlength,
int *sum_length);
bigdig_t *
be_power_arr(const bigdig_t *n, int nlength, int exp, int *res_length);
void
betest_orig(void);
void
betest_trim(void);
void
betest_show(bigdig_pc num,int len,const char *sym);
bgn_p
bgninit(const char *tag,u32 opt,int maxlen,...);
bgn_p
bgnacquire(bgn_p num);
bgn_p
bgnrelease(bgn_p num);
void
bgngrow(bgn_p num,int newlen);
void
bgnshrink(bgn_p num);
void
lecopy(bgn_p to,bgn_pc from);
bgn_p
lepad(bgn_pc num,int newlen);
void
letrim(bgn_p num);
int
_letrim(bgn_pc num);
void
leshow(bgn_pc num,u32 opt,const char *who);
void
lesumeq(bgn_p result,bgn_p op2);
bgn_p
le_power_arr_eq(bgn_pc num,int exp);
bgn_p
lesumrt(bgn_p n,bgn_p m);
bgn_p
le_power_arr_rt(bgn_pc n, int exp);
void
letest_lesumrt(void);
void
letest_lesumeq(void);
void
_sysfault(void);
void
reverse(bigdig_p arr, int length);
#define TSTDEF(_on,_fnc) \
{ .tst_allow = _on, .tst_tag = #_fnc, .tst_fnc = _fnc }
#ifdef TSTDEFALL
tstctl_t tstlist[] = {
TSTDEF(1,betest_orig),
TSTDEF(0,betest_trim),
TSTDEF(1,letest_lesumrt),
TSTDEF(1,letest_lesumeq),
{ .tst_tag = NULL }
};
#endif
#endif
// badfree/bemath -- big endian math
void
becopy(bigdig_t *to, const bigdig_t *from, int length)
{
int i;
for (i = 0; i < length; i++)
to[i] = from[i];
}
bigdig_t *
bepad(const bigdig_t *n, int nlength, int new_length, enum SIDE side)
{
int i;
int j;
bigdig_t *padded;
if (nlength < 1 || new_length <= nlength) {
sysfault("bepad: length fault -- nlength=%d new_length=%d\n",
nlength,new_length);
return NULL;
}
padded = calloc(new_length,sizeof(bigdig_t));
if (!padded)
return NULL;
if (side == LOW) {
j = new_length - 1;
for (i = (nlength - 1); i >= 0; i--)
padded[j--] = n[i];
}
else {
j = 0;
for (i = 0; i < nlength; i++)
padded[j++] = n[i];
}
return padded;
}
bigdig_t *
betrim(const bigdig_t *n, int *res_length)
{
int i;
int j;
int nlength;
bigdig_t *res;
nlength = *res_length;
for (i = 0; i < nlength; i++) {
if (n[i] > 0)
break;
}
nlength -= i;
*res_length = nlength;
res = malloc(sizeof(bigdig_t) * nlength);
if (!res) {
sysfault("betrim: null malloc\n");
return NULL;
}
j = 0;
for (; i < nlength; ++i, ++j)
res[j] = n[i];
return res;
}
bigdig_t *
besum(bigdig_t *n, int nlength, bigdig_t *m, int mlength,
int *sum_length)
{
int i;
int tmp;
int carry;
bigdig_t *result;
bigdig_t *trimmed;
bigdig_t *op1;
bigdig_t *op2;
int padflg;
enum SIDE side = LOW;
// NOTE: here op2 comes from _caller_ -- so do _not_ free it in this
// function -- _this_ is the cause of the bug
// case (1)
if (nlength == mlength) {
op1 = n;
op2 = m;
padflg = 0;
}
// NOTE: here op2 comes from _pad_ -- so we do _want_ to free it so it
// doesn't leak
// case (2)
else if (nlength > mlength) {
op1 = n;
op2 = bepad(m, mlength, nlength, side);
padflg = 1;
}
// case (3)
else {
op1 = m;
op2 = bepad(n, nlength, mlength, side);
padflg = 2;
}
result = malloc(sizeof(bigdig_t) * (MAX(nlength, mlength) + 1));
if (!op1 || !op2 || !result) {
sysfault("besum: null fault -- padflg=%d op1=%p op2=%p result=%p\n",
padflg,op1,op2,result);
if (padflg)
FREEME(op2);
FREEME(result);
return NULL;
}
carry = 0;
for (i = (MAX(nlength, mlength)) - 1; i >= 0; i--) {
tmp = op1[i] + op2[i] + carry;
if (carry > 0)
carry = 0;
if (tmp >= 10) {
carry = tmp / 10;
tmp = tmp % 10;
}
result[i + 1] = tmp;
}
// NOTE: we want to free op2 for case (2)/(3) but we didn't remember
// how we got it: (1) means no free, (2)/(3) means free
// only free if this if we called bepad, and _not_ if this pointer belongs
// to caller
if (padflg)
FREEME(op2);
if (carry > 0)
result[0] = carry;
*sum_length = (MAX(nlength, mlength)) + 1;
trimmed = betrim(result, sum_length);
FREEME(result);
return trimmed;
}
bigdig_t *
be_power_arr(const bigdig_t *n, int nlength, int exp, int *res_length)
{
bigdig_t *tmp;
bigdig_t *rt;
bigdig_t *bufp;
int bufp_length;
int i;
// NOTE: rt/bufp are memory leaks -- they are never freed
rt = malloc(sizeof(bigdig_t) * 1000);
bufp = malloc(sizeof(bigdig_t) * 1000);
// NOTE: this is a memory leak -- if one is null, but the other is non-null,
// you must free the non-null one or it leaks
if (!rt || !bufp) {
FREEME(rt);
FREEME(bufp);
return NULL;
}
becopy(rt, n, nlength);
becopy(bufp, n, nlength);
*res_length = bufp_length = nlength;
while (--exp > 0) {
for (i = *n - 1; i > 0; i--) {
tmp = besum(rt, nlength, bufp, bufp_length, res_length);
if (tmp == NULL)
sysfault("be_power_arr: null besum return\n");
nlength = *res_length;
becopy(rt, tmp, nlength);
// NOTE: this will now work because of the padflg changes in
// besum
#if 0
// free(tmp); // produces undefined output?
#else
FREEME(tmp);
#endif
}
becopy(bufp, rt, *res_length);
bufp_length = *res_length;
}
FREEME(bufp);
return rt;
}
// badfree/betest -- big endian tests
void
betest_orig(void)
{
#if 1
bigdig_t b[] = { 3 };
#else
const bigdig_t b[] = { 3 };
#endif
int rlength;
int exp;
bigdig_t *res;
exp = 3;
printf("\n");
printf("betest_orig: exp=%d\n",exp);
betest_show(b,1,"b");
rlength = 0;
res = be_power_arr(b, 1, exp, &rlength);
betest_show(res,rlength,"res");
FREEME(res);
}
void
betest_trim(void)
{
int i;
int j;
int length;
const bigdig_t n[] = { 4, 5, 6 };
const bigdig_t m[] = { 0, 3, 5 };
const bigdig_t *num;
bigdig_t *trimmed;
printf("\n");
printf("betest_trim:\n");
for (i = 0; i < 10; i++) {
num = (i % 2 == 0) ? n : m;
length = 3;
trimmed = betrim(num, &length);
if (!trimmed)
sysfault("betest_trim: trimmed was null\n");
printf("pass %d: num=%p trimmed=%p\n",i,num,trimmed);
for (j = 0; j < length; j++)
printf(" %d", trimmed[j]);
printf("\n");
FREEME(trimmed);
}
}
// betest_show -- show number
void
betest_show(bigdig_pc num,int len,const char *sym)
{
int i;
printf(" sym %s length %d --",sym,len);
for (i = 0; i < len; i++)
printf(" %d",num[i]);
printf("\n");
}
// badfree/bgn -- big number control
// bgninit -- create new number
bgn_p
bgninit(const char *tag,u32 opt,int maxlen,...)
// opt -- options (BGNINIT* -- has initializer data)
// maxlen -- length of number
{
va_list ap;
int i;
bgn_pc from;
bgn_p num;
va_start(ap,maxlen);
num = calloc(1,sizeof(bgn_t));
opt |= BGNASTRUCT;
opt |= BGNABASE;
num->num_opts = opt;
num->num_tag = tag;
num->num_refcnt = 1;
if (maxlen <= 0)
maxlen = 1;
from = NULL;
if (opt & BGNINITDUP) {
from = va_arg(ap,bgn_pc);
maxlen = MAX(maxlen,from->num_curlen);
}
num->num_maxlen = maxlen;
num->num_base = calloc(maxlen,sizeof(bigdig_t));
// initialize from varargs
if (opt & BGNINITAV) {
for (i = 0; i < maxlen; ++i)
num->num_base[i] = va_arg(ap,int);
num->num_curlen = maxlen;
}
// initialize by cloning data
if (opt & BGNINITDUP) {
maxlen = from->num_curlen;
for (i = 0; i < maxlen; ++i)
num->num_base[i] = from->num_base[i];
num->num_curlen = maxlen;
}
va_end(ap);
return num;
}
// bgnacquire -- increment reference count
bgn_p
bgnacquire(bgn_p num)
{
num->num_refcnt += 1;
return num;
}
// bgnrelease -- decrement reference count and deallocate
bgn_p
bgnrelease(bgn_p num)
{
if (--num->num_refcnt == 0) {
if (num->num_opts & BGNABASE)
FREEME(num->num_base);
if (num->num_opts & BGNASTRUCT)
FREEME(num);
// this zaps caller's pointer
num = NULL;
}
return num;
}
// bgngrow -- grow allocated number to given length
void
bgngrow(bgn_p num,int newlen)
{
int growlen;
int maxlen;
int i;
BGNASSERT(num,BGNABASE);
maxlen = num->num_maxlen;
growlen = newlen - maxlen;
if (growlen > 0) {
maxlen += growlen;
num->num_base = realloc(num->num_base,sizeof(bigdig_t) * maxlen);
// zero extend the new area
for (i = num->num_maxlen; i < maxlen; ++i)
num->num_base[i] = 0;
num->num_maxlen = maxlen;
}
}
// bgnshrink -- shrink allocated number to current length
void
bgnshrink(bgn_p num)
{
int curlen;
BGNASSERT(num,BGNABASE);
curlen = num->num_curlen;
if (num->num_maxlen > curlen) {
num->num_base = realloc(num->num_base,sizeof(bigdig_t) * curlen);
num->num_maxlen = curlen;
}
}
// badfree/lecom -- little endian math common
// lecopy -- copy big number
void
lecopy(bgn_p to,bgn_pc from)
{
int newlen;
int i;
dbgprtf("lecopy: ENTER\n");
dbgleshow(to,"lecopy");
dbgleshow(from,"lecopy");
newlen = from->num_curlen;
bgngrow(to,newlen);
for (i = 0; i < newlen; ++i)
to->num_base[i] = from->num_base[i];
to->num_curlen = newlen;
dbgleshow(to,"lecopy");
dbgprtf("lecopy: EXIT\n");
}
// lepad -- clone and pad number
bgn_p
lepad(bgn_pc num,int newlen)
{
int i;
int curlen;
bgn_p padded;
curlen = num->num_curlen;
#if 0
if ((curlen < 1) || (newlen <= curlen)) {
sysfault("lepad: length fault -- curlen=%d newlen=%d\n",curlen,newlen);
return NULL;
}
#endif
BGNINIT(padded,0,newlen);
if (!padded) {
sysfault("lepad: bgninit returned null\n");
return NULL;
}
// copy existing digits
for (i = 0; i < curlen; ++i)
padded->num_base[i] = num->num_base[i];
// zero extend the larger number
for (; i < newlen; ++i)
padded->num_base[i] = 0;
return padded;
}
// letrim -- get rid of leading zeroes by adjusting current length
void
letrim(bgn_p num)
{
num->num_curlen = _letrim(num);
}
// _letrim -- get rid of leading zeroes by adjusting current length
// RETURNS: trimmed length
int
_letrim(bgn_pc num)
{
int i;
int curlen;
curlen = num->num_curlen;
for (i = curlen - 1; i >= 0; --i) {
if (num->num_base[i] > 0)
break;
}
if (i <= 0)
i = 1;
return i;
}
// leshow -- show number
void
leshow(bgn_pc num,u32 opt,const char *who)
{
int curlen;
int i;
if (opt & BGNTRIM)
curlen = _letrim(num);
else
curlen = num->num_curlen;
if (who != NULL)
printf("%s: ",who);
printf("sym=%s ref=%d len=%d/%d",
num->num_tag,num->num_refcnt,curlen,num->num_maxlen);
printf(" trim=%s order=%s",
(opt & BGNTRIM) ? "yes" : "no",
(opt & BGNRAW) ? "raw" : "flip");
printf("\n");
if (who != NULL)
printf("%s:",who);
printf(" ");
if (opt & BGNRAW) {
for (i = 0; i < curlen; ++i)
printf(" %d",num->num_base[i]);
}
else {
for (i = curlen - 1; i >= 0; --i)
printf(" %d",num->num_base[i]);
}
printf("\n");
}
// badfree/lesumeq -- little endian sum / power (in-place)
// lesumeq -- do x += y
void
lesumeq(bgn_p result,bgn_p op2)
{
int op2len;
int i;
int tmp;
int carry;
int maxlen;
int minlen;
op2len = op2->num_curlen;
tmp = result->num_curlen;
maxlen = MAX(tmp,op2len);
minlen = MIN(tmp,op2len);
bgngrow(result,maxlen + 1);
carry = 0;
i = 0;
for (; i < minlen; ++i) {
tmp = result->num_base[i] + op2->num_base[i] + carry;
if (tmp >= 10) {
carry = tmp / 10;
tmp %= 10;
}
else
carry = 0;
result->num_base[i] = tmp;
}
++maxlen;
for (; i < maxlen; ++i) {
if (! carry)
break;
tmp = result->num_base[i] + carry;
if (tmp >= 10) {
carry = tmp / 10;
tmp %= 10;
}
else
carry = 0;
result->num_base[i] = tmp;
}
result->num_curlen = maxlen;
}
// le_power_arr_eq -- raise number to power
bgn_p
le_power_arr_eq(bgn_pc num,int exp)
{
bgn_p rtpwr;
bgn_p bufp;
int icur;
int ilim;
BGNINIT(rtpwr,BGNINITDUP,1000,num);
BGNINIT(bufp,BGNINITDUP,1000,num);
ilim = num->num_base[0];
ilim -= 1;
while (--exp > 0) {
for (icur = 0; icur < ilim; ++icur)
lesumeq(rtpwr,bufp);
lecopy(bufp,rtpwr);
}
BGNRELEASE(bufp);
return rtpwr;
}
// badfree/lesumrt -- little endian sum / power (alloc/return mode)
bgn_p
lesumrt(bgn_p n,bgn_p m)
{
int i;
int tmp;
int carry;
int maxlen;
bgn_p rtsum;
bgn_p op1;
bgn_p op2;
dbgprtf("lesumrt: ENTER\n");
dbgleshow(n,"lesumrt");
dbgleshow(m,"lesumrt");
// case (1)
if (n->num_curlen == m->num_curlen) {
op1 = BGNACQUIRE(n);
op2 = BGNACQUIRE(m);
}
// case (2)
else if (n->num_curlen > m->num_curlen) {
op1 = BGNACQUIRE(n);
op2 = lepad(m,n->num_curlen);
}
// case (3)
else {
op1 = BGNACQUIRE(m);
op2 = lepad(n,m->num_curlen);
}
maxlen = MAX(n->num_curlen,m->num_curlen);
dbgprtf("lesumrt: PAD maxlen=%d\n",maxlen);
BGNINIT(rtsum,0,maxlen + 1);
carry = 0;
for (i = 0; i < maxlen; ++i) {
tmp = op1->num_base[i] + op2->num_base[i] + carry;
if (tmp >= 10) {
carry = tmp / 10;
tmp %= 10;
}
else
carry = 0;
rtsum->num_base[i] = tmp;
}
rtsum->num_base[i] += carry;
rtsum->num_curlen = maxlen + 1;
BGNRELEASE(op1);
BGNRELEASE(op2);
dbgleshow(rtsum,"lesumrt");
dbgprtf("lesumrt: EXIT\n");
return rtsum;
}
bgn_p
le_power_arr_rt(bgn_pc n, int exp)
{
bgn_p tmp;
bgn_p rtpwr;
bgn_p bufp;
int icur;
int ilim;
dbgprtf("le_power_arr_rt: ENTER\n");
BGNINIT(rtpwr,BGNINITDUP,1000,n);
BGNINIT(bufp,BGNINITDUP,1000,n);
ilim = n->num_base[0];
ilim -= 1;
while (--exp > 0) {
for (icur = 0; icur < ilim; ++icur) {
tmp = lesumrt(rtpwr,bufp);
if (tmp == NULL)
sysfault("le_power_arr_rt: null lesumrt return\n");
lecopy(rtpwr,tmp);
BGNRELEASE(tmp);
}
lecopy(bufp,rtpwr);
}
BGNRELEASE(bufp);
dbgprtf("le_power_arr_rt: EXIT\n");
return rtpwr;
}
// badfree/letest -- little endian tests
void
letest_lesumrt(void)
{
bgn_p b;
int exp;
bgn_p res;
exp = 3;
printf("\n");
printf("letest_lesumrt: exp=%d\n",exp);
BGNINIT(b,BGNINITAV,1,3);
leshow(b,0,"letest_lesumrt");
res = le_power_arr_rt(b,exp);
leshow(res,0,"letest_lesumrt");
BGNRELEASE(res);
BGNRELEASE(b);
}
void
letest_lesumeq(void)
{
bgn_p b;
int exp;
bgn_p res;
exp = 3;
printf("\n");
printf("letest_lesumeq: exp=%d\n",exp);
BGNINIT(b,BGNINITAV,1,3);
leshow(b,0,"letest_lesumeq");
res = le_power_arr_eq(b,exp);
leshow(res,0,"letest_lesumeq");
BGNRELEASE(res);
BGNRELEASE(b);
}
// badfree/util -- utility functions
void
_sysfault(void)
{
exit(1);
}
void
reverse(bigdig_p arr, int length)
{
int lhs;
int rhs;
bigdig_t tmp;
for (lhs = 0, rhs = length - 1; lhs < rhs; ++lhs, --rhs) {
tmp = arr[lhs];
arr[lhs] = arr[rhs];
arr[rhs] = tmp;
}
}
int opt_fork;
int opt_T;
// main -- main program
int
main(int argc, char **argv)
{
char *cp;
tstctl_p tst;
int tstno;
--argc;
++argv;
for (; argc > 0; --argc, ++argv) {
cp = *argv;
if (*cp != '-')
break;
switch (cp[1]) {
case 'f':
opt_fork = 1;
break;
case 'h':
usage();
break;
case 'T':
opt_T = atoi(cp + 2);
break;
default:
usage();
break;
}
}
if (opt_T <= 0)
opt_T = 1;
for (FORTSTALL(tst))
tst->tst_allow = (argc <= 0);
for (; argc > 0; --argc, ++argv) {
cp = *argv;
for (FORTSTALL(tst)) {
if (strcmp(tst->tst_tag, cp) == 0)
tst->tst_allow = 1;
}
}
for (tstno = 0; tstno < opt_T; ++tstno)
dotests();
return 0;
}
// dotests -- perform tests
void
dotests(void)
{
tstctl_p tst;
pid_t pid;
int status;
for (FORTSTALL(tst)) {
if (!tst->tst_allow)
continue;
if (!opt_fork) {
dotest(tst);
continue;
}
pid = fork();
if (pid) {
waitpid(pid, &status, 0);
continue;
}
dotest(tst);
exit(0);
}
}
// dotest -- perform test
void
dotest(tstctl_p tst)
{
tst->tst_fnc();
}
// usage -- show usage
void
usage(void)
{
tstctl_pc tst;
printf("usage: [options] [test names]\n");
printf("\n");
printf("options:\n");
printf(" -f -- run tests in forked child\n");
printf(" -T<repeat> -- test repeat count\n");
printf("\n");
printf("tests:\n");
for (FORTSTALL(tst))
printf(" %s\n", tst->tst_tag);
exit(1);
}