Fresh new start.

This commit is contained in:
Nicolás Ortega Froysa 2018-10-18 12:00:32 +02:00
parent 7aceb7fcc7
commit 01a32c0e52
No known key found for this signature in database
GPG Key ID: FEC70E3BAE2E69BF
13 changed files with 76 additions and 3161 deletions

View File

@ -1,8 +0,0 @@
Authors
=======
These are a list of people that have contributed to the Uhn project.
| **Name/Alias** | **E-Mail** |
|---------------------------|---------------------------------------|
| Deathsbreed | deathsbreed@themusicinnoise.net |

View File

@ -36,3 +36,7 @@ Change Log
- Exporting files uses less memory - Exporting files uses less memory
- Allow searching for the nth prime number - Allow searching for the nth prime number
- Clearing some memory leaks - Clearing some memory leaks
- v2.0: Linked List
- Implement linked list for faster addition to list.
- Multi-Processor support (MPI).
- Code cleanup.

View File

@ -20,7 +20,7 @@ project(Indivisible)
set(TARGET_NAME indivisible) set(TARGET_NAME indivisible)
if(NOT CMAKE_BUILD_TYPE) if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "debug") set(CMAKE_BUILD_TYPE "release")
endif() endif()
string(TOLOWER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE) string(TOLOWER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE)
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
@ -30,18 +30,15 @@ set(CMAKE_MODULE_PATH
${CMAKE_SOURCE_DIR}/cmake/) ${CMAKE_SOURCE_DIR}/cmake/)
find_package(GMP REQUIRED) find_package(GMP REQUIRED)
find_package(OpenMP REQUIRED)
include_directories( include_directories(
${GMP_INCLUDE_DIR}) ${GMP_INCLUDE_DIR})
set(SRCS set(SRCS
src/main.c src/main.c)
src/files.c
src/list.c)
# Define the C flags. # Define the C flags.
set(CMAKE_C_FLAGS "-std=gnu99 ${OpenMP_C_FLAGS} -Wall -Wextra -Werror -Wfatal-errors -Wmissing-declarations -pedantic-errors") set(CMAKE_C_FLAGS "-std=gnu99 -Wall -Wextra -Werror -Wfatal-errors -Wmissing-declarations -pedantic-errors")
set(CMAKE_C_FLAGS_DEBUG "-g -O0") set(CMAKE_C_FLAGS_DEBUG "-g -O0")
set(CMAKE_C_FLAGS_RELEASE "-O3") set(CMAKE_C_FLAGS_RELEASE "-O3")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "-g -O3") set(CMAKE_C_FLAGS_RELWITHDEBINFO "-g -O3")
@ -49,6 +46,8 @@ set(CMAKE_C_FLAGS_MINSIZEREL "-Os")
if(NOT CMAKE_BUILD_TYPE MATCHES "debug" AND NOT CMAKE_BUILD_TYPE MATCHES "relwithdebinfo") if(NOT CMAKE_BUILD_TYPE MATCHES "debug" AND NOT CMAKE_BUILD_TYPE MATCHES "relwithdebinfo")
add_definitions("-DNDEBUG") add_definitions("-DNDEBUG")
else()
add_definitions("-DDEBUG")
endif() endif()
add_executable(${TARGET_NAME} ${SRCS}) add_executable(${TARGET_NAME} ${SRCS})

View File

@ -1,23 +0,0 @@
Contributing
============
We gladly accept contributions to Indivisible (send Git patches to <deathsbreed@themusicinnoise.net>), however there are a few guidelines that must be followed.
1) Copyright & Licensing
------------------------
The copyright of this project and all of its code, unless stated otherwise, belongs to Nicolás Ortega and is licensed with the [GNU GPLv3](/LICENSE). This also applies to all contributions (contributors may add themselves to the [authors file](/AUTHORS.md) for recognition). This is purely for legal purposes, there is no intention to deny your contributions.
2) Documentation
----------------
All new features added must be well documented using DOxygen style comments. If your patch adds a new feature and it is not well documented we will ask you to finish documenting the code before accepting a patch. This is simply so that both the maintainers of this repo and other users can understand the new API that bas been added.
3) Code-Style
-------------
The only aspects that will be religiously upheld for all contributions to this repo are indentation and bracket placement. All indentation must be tab characters (reason being so that everyone can view the code with the indentation that they prefer rather than being forced to see a specific indentation), and brackets must be attached. Everything else is minor and can be overlooked.
Things that would be nice is if you could avoid having the lines being too long, and please avoid having more than one empty line in a row.
4) Fractured Patches
--------------------
Please make sure your patches are fractured into separate parts so that it may be easy for us to deal with them individually. The reason for this is to avoid having patches that both fix bugs and add features when in reality they should be separate requests. The only exception to this will be if the changes made to the code depend on one another to function properly.
If your patch meets all these requirements and is passed by one of the moderators it will be merged into the main repo.

2489
Doxyfile

File diff suppressed because it is too large Load Diff

32
README Normal file
View File

@ -0,0 +1,32 @@
===================
*** Indivisible ***
===================
Indivisible is a parallelized prime number generator written in C.
# Building
----------
There are multiple dependencies to install before compiling the project:
- CMake build system
- GNU Multi-Precision Arithmetics Library
Once the dependencies are installed you can compile by running the following
from the root directory of the project:
cd build/
cmake ..
make
This will create a release build, to build with debug options append the
`-DCMAKE_BUILD_TYPE=debug' flag to the `cmake' command.
# Contributing
--------------
To contribute to indivisible you can send patch files to my e-mail[0].
[0] nortega@themusicinnoise.net
# License
---------
This project is licensed under the GNU General Public License version 3 or
greater (see `LICENSE' for more information).

View File

@ -1,29 +0,0 @@
Indivisible
===========
[![build status](https://gitlab.com/Deathsbreed/Indivisible/badges/master/build.svg)](https://gitlab.com/Deathsbreed/Indivisible/commits/master)
Indivisible is an optimized prime number generator written in C.
Building
--------
There are multiple dependencies to install before compiling the project:
- CMake
- GMP
- OpenMP
Once the dependencies are installed you can compile by running the following from the root directory of the project:
```bash
$ cd build/
$ cmake ..
$ make
```
To build a release build run `cmake -DCMAKE_BUILD_TYPE=Release ..` instead.
Contributing
------------
If you would like to contribute to Indivisible then please read the [contributing file](/CONTRIBUTING.md) before sending a patch. You may send the patches to <deathsbreed@themusicinnoise.net>.
License
-------
This project is licensed with the [GNU GPLv3](/LICENSE). Since it is an educational project I find it very important that all contributions continue to remain free/libre.

View File

@ -1,3 +0,0 @@
--lineend=linux
--indent=tab
--style=attach

View File

@ -1,85 +0,0 @@
/*
* Copyright (C) 2017 Ortega Froysa, Nicolás <deathsbreed@themusicinnoise.net>
* Author: Ortega Froysa, Nicolás <deathsbreed@themusicinnoise.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "files.h"
#include <stdio.h>
#include <gmp.h>
#include <assert.h>
int inputPrimes(char *file, List *list) {
// Assert safeguards
assert(file);
assert(list);
printf("Loading primes from `%s'...\n", file);
FILE *in = fopen(file, "r");
if(!in) return 1;
mpz_t n;
mpz_init(n);
while(mpz_inp_raw(n, in))
if(addToList(list, n) == 1) return 3;
mpz_clear(n);
if(fclose(in)) return 2;
return 0;
}
int outputPrimes(char *file, List *list, size_t startPos) {
// Assert safeguards
assert(file);
assert(list);
FILE *out = fopen(file, "a");
if(!out) return 1;
printf("Saving primes to `%s'...\n", file);
puts("0%");
for(size_t i = startPos; i < list->end; ++i) {
if(!mpz_out_raw(out, list->list[i])) return 3;
if(i - startPos == (list->end - startPos) / 4) puts("25%");
else if(i - startPos == (list->end - startPos) / 2) puts("50%");
else if(i - startPos == (list->end - startPos) * 3 / 4) puts("75%");
}
puts("100%");
if(fclose(out)) return 2;
return 0;
}
int exportPrimes(char *efile, char *dfile, int base) {
// Assert safeguards
assert(efile);
assert(dfile);
FILE *in = fopen(dfile, "r");
FILE *out = fopen(efile, "w");
if(!in || !out) return 1;
printf("Exporting primes to `%s'...\n", efile);
mpz_t n;
mpz_init(n);
while(mpz_inp_raw(n, in)) {
if(!mpz_out_str(out, base, n)) return 3;
fprintf(out, "\n");
}
if(fclose(in) || fclose(out)) return 2;
return 0;
}

View File

@ -1,57 +0,0 @@
/*
* Copyright (C) 2017 Ortega Froysa, Nicolás <deathsbreed@themusicinnoise.net>
* Author: Ortega Froysa, Nicolás <deathsbreed@themusicinnoise.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file files.h
* @author Deathsbreed <deathsbreed@themusicinnoise.net>
* @brief Functions to deal with file I/O of primes.
* @details Functions that input, output, and export primes from/to files.
*/
#pragma once
#include "list.h"
/**
* @brief Load primes from an Indivisible file into a List.
* @param file File to input primes from.
* @param list List to load primes into.
* @returns If 0 then load was successful, if 1 then failed to open,
* if 2 failed to close, if 3 failed to allocate new memory to List
* (see `addToList()')
*/
int inputPrimes(char *file, List *list);
/**
* @brief Output primes from a List into an Indivisible file.
* @param file File to output primes to.
* @param list List to read primes from.
* @param startPos The position in the List of primes at which to append
* to the file.
* @returns If 0 then load was successful, if 1 then failed to open,
* if 2 failed to close, if 3 failed when writing.
*/
int outputPrimes(char *file, List *list, size_t startPos);
/**
* @brief Export primes from a List to a plain text file.
* @param efile File to export primes as plain text to.
* @param dfile File to read primes from.
* @param base The base in which the primes will be written.
* @returns If 0 then load was successful, if 1 then failed to open,
* if 2 failed to close, if 3 failed when writing.
*/
int exportPrimes(char *efile, char *dfile, int base);

View File

@ -1,54 +0,0 @@
/*
* Copyright (C) 2017 Ortega Froysa, Nicolás <deathsbreed@themusicinnoise.net>
* Author: Ortega Froysa, Nicolás <deathsbreed@themusicinnoise.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "list.h"
#include <stdlib.h>
/**
* This is the number of elements by which the list expands.
*/
#ifndef BLOCK_SIZE
#define BLOCK_SIZE 132120576
#endif
int initList(List *restrict l) {
l->list = malloc(sizeof(mpz_t) * BLOCK_SIZE);
if(!l->list) return 1;
l->size = BLOCK_SIZE;
l->end = 0;
return 0;
}
void deInitList(List *restrict l) {
for(size_t i = 0; i < l->end; ++i) {
mpz_clear(l->list[i]);
}
free(l->list);
}
int addToList(List *restrict l, mpz_t n) {
if(l->end == l->size) {
l->size += BLOCK_SIZE;
void *tmp = realloc(l->list, sizeof(mpz_t) * l->size);
if(!tmp) return 1;
l->list = tmp;
}
mpz_init(l->list[l->end]);
mpz_set(l->list[l->end++], n);
return 0;
}

View File

@ -1,61 +0,0 @@
/*
* Copyright (C) 2017 Ortega Froysa, Nicolás <deathsbreed@themusicinnoise.net>
* Author: Ortega Froysa, Nicolás <deathsbreed@themusicinnoise.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file list.h
* @author Deathsbreed <deathsbreed@themusicinnoise.net>
* @brief Code responsible for List management.
* @details Code responsible for the definition and management of the
* List object.
*/
#pragma once
#include <gmp.h>
/**
* @brief An infinitely expanding list type.
*/
typedef struct {
mpz_t *list; //!< The list of elements
size_t size; //!< How many elements are in the list
size_t end; //!< The last element of the list (in use)
} List;
/**
* @brief Initialize a List.
* @details Initialize the list and its variables, allocating memory
* to the pointer array inside.
* @param[in] l A pointer to a List type to be initialized.
* @returns Returns 0 if successful and 1 if failed.
*/
int initList(List *restrict l);
/**
* @brief Deinitialize a List.
* @details Release all memory that has been allocated to the list.
* @param[in] l A pointer to a List type to be deinitialized.
*/
void deInitList(List *restrict l);
/**
* @brief Adds a new item to a List type.
* @details Add item `n' at the end of a List type.
* @param[out] l List to which the variable should be appended.
* @param[in] n variable to be appended to the list.
* @returns Returns 0 if successful and 1 if failed.
*/
int addToList(List *restrict l, mpz_t n);

View File

@ -1,311 +0,0 @@
/*
* Copyright (C) 2017 Ortega Froysa, Nicolás <deathsbreed@themusicinnoise.net>
* Author: Ortega Froysa, Nicolás <deathsbreed@themusicinnoise.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <stdbool.h>
#include <unistd.h>
#include <string.h>
#include <gmp.h>
#include <omp.h>
#include <sys/stat.h>
#include "list.h"
#include "files.h"
#define VERSION "v1.0"
static bool run;
void printUsage(char *progName);
void leave();
int main(int argc, char *argv[]) {
// Variables for argument parsing
bool f_help = false,
f_version = false,
f_quiet = false;
int base = 10;
size_t n_prime = 0;
char *dfile = NULL;
char *efile = NULL;
// Parse commandline arguments
int c;
while((c = getopt(argc, argv, "hvqb:f:e:n:")) != -1) {
switch(c) {
case 'h':
f_help = true;
break;
case 'v':
f_version = true;
break;
case 'q':
f_quiet = true;
break;
case 'b':
base = atoi(optarg);
if(base < 2 || base > 62) {
fprintf(stderr,
"Invalid base `%d'. Base must be between 2 and 62.\n",
base);
return 1;
}
break;
case 'f':
dfile = optarg;
break;
case 'e':
efile = optarg;
break;
case 'n':
n_prime = atoi(optarg);
if(n_prime <= 2) {
fprintf(stderr,
"`n' must be larger than 2 (first two primes are 2 and 3).\n");
return 1;
}
break;
default:
printUsage(argv[0]);
return 1;
}
}
// Act based on which flags were used
if(f_help) {
printUsage(argv[0]);
puts("\nArguments:");
puts(" -h print this help information");
puts(" -v print version number of program");
puts(" -q quiet mode");
puts(" -b <base> base in which to print primes between 2 and 62 (default 10)");
puts(" -f <file> file in/from which primes are stored and read from in raw format");
puts(" -e <file> export input file to plain text format");
puts(" -n <n> run until the 'n'th prime\n");
return 0;
} else if(f_version) {
printf("Indivisible %s\n", VERSION);
return 0;
} else if(f_quiet && !dfile) {
puts("Error: you cannot run in quiet mode without specifying a data file.");
printUsage(argv[0]);
return 0;
}
if(efile && !dfile) {
puts("Error: you must have a file to export to.");
printUsage(argv[0]);
return 0;
} else if(efile && dfile) {
if(strcmp(efile, dfile) == 0) {
fprintf(stderr, "Error: file paths are the same.\n");
return 0;
}
int err = exportPrimes(efile, dfile, base);
if(err) {
if(err == 1) fprintf(stderr, "Error: failed to open files.\n");
else if(err == 2) fprintf(stderr, "Error: failed to close files.\n");
else if(err == 3) fprintf(stderr, "Error: failed to write to export file.\n");
return 1;
}
return 0;
}
if(!omp_get_cancellation()) {
puts("Warning: the OpenMP cancellation environment variable (`OMP_CANCELLATION') is not enabled.");
char in;
while(true) {
printf("[e]nable/[c]ontinue/[q]uit? ");
scanf("%c", &in);
if(in == 'e' || in == 'E') {
putenv("OMP_CANCELLATION=true");
execv(argv[0], argv);
} else if(in == 'c' || in == 'C') {
break;
} else if(in == 'q' || in == 'Q') {
return 0;
}
}
}
// Quit on ^C by setting `run = false'
run = true;
signal(SIGINT, leave);
puts("Use Ctrl+C to exit.");
bool newFile = true;
if(dfile) {
struct stat s;
if(stat(dfile, &s) == 0) newFile = false;
}
// Last position added from file
size_t startPos = 0;
int exitCode = 0;
// Primes we've found
List primes;
if(initList(&primes) == 1) {
fprintf(stderr, "Failed to initialize primes list.\n");
exit(1);
}
// The number we're going to be testing for
mpz_t num;
mpz_init(num);
// Variable for sqrt of `num'
mpz_t numRoot;
mpz_init(numRoot);
// Index of the prime number above and closest to `numRoot'
size_t rootInd = 0;
if(newFile) {
// Add 2, a known prime to this list
mpz_set_ui(num, 2);
if(addToList(&primes, num) == 1) {
fprintf(stderr, "Failed to allocate more memory for list.\n");
exitCode = 1;
goto releaseMemory;
}
// Add 3 as well to optimize the algorithm
mpz_set_ui(num, 3);
if(addToList(&primes, num) == 1) {
fprintf(stderr, "Failed to allocate more memory for list.\n");
exitCode = 1;
goto releaseMemory;
}
if(!f_quiet) {
puts("2\n3");
}
} else {
// Load primes from file
int err = inputPrimes(dfile, &primes);
startPos = primes.end;
if(err == 0) {
printf("Loaded %zu primes.\n", primes.end);
} else {
if(err == 1)
fprintf(stderr, "Failed to open Indivisible file `%s'.\n", dfile);
else if(err == 2)
fprintf(stderr, "Failed to close Indivisible file `%s'.\n", dfile);
else if(err == 3)
fprintf(stderr, "Failed to allocate more memory for list.\n");
exitCode = 1;
goto releaseMemory;
}
/**
* I set to `primes.end-1' because primes.end indicates the next new
* element in the list that can be used, which is also equal to the total
* number of elements in the list.
*/
mpz_set(num, primes.list[primes.end-1]);
}
while(run) {
mpz_add_ui(num, num, 2);
// Calculate the sqrt(num)
mpz_sqrt(numRoot, num);
while(mpz_cmp(primes.list[rootInd], numRoot) <= 0) {
if(++rootInd > primes.end) {
fprintf(stderr, "Error: `rootInd' surpassed `primes.end'.\n");
exitCode = 1;
goto releaseMemory;
}
}
bool isPrime = true;
/**
* Loop through primes we've found until we get to the sqrt of the
* number we're analyzing. Also, skip `2' since we're not testing even
* numbers.
*/
#pragma omp parallel
{
#pragma omp for
for(size_t i = 1; i < rootInd; ++i) {
if(mpz_divisible_p(num, primes.list[i])) {
#pragma omp atomic write
isPrime = false;
#pragma omp cancel for
}
#pragma omp cancellation point for
}
}
if(isPrime) {
// `num' is a prime so we add it to the list and print it
if(addToList(&primes, num) == 1) {
fprintf(stderr, "Failed to allocate more memory for list.\n");
exitCode = 1;
goto releaseMemory;
}
if(!f_quiet) {
if(!mpz_out_str(stdout, base, num))
fprintf(stderr, "Could not print to `stdout'!\n");
printf("\n");
}
}
if(primes.end == n_prime) break;
}
printf("Found %zu primes.\n", primes.end);
if(dfile) {
int err = outputPrimes(dfile, &primes, startPos);
if(err == 0) {
puts("Successfully saved primes.");
} else {
if(err == 1)
fprintf(stderr, "Failed to open/create file `%s'.\n", dfile);
else if(err == 2)
fprintf(stderr, "Failed to close file `%s'.\n", dfile);
else if(err == 3)
fprintf(stderr, "Failed while writing a prime to `%s'.\n", dfile);
exitCode = 1;
goto releaseMemory;
}
}
releaseMemory:
puts("Clearing memory...");
// Clear GMP variables
mpz_clear(numRoot);
mpz_clear(num);
// Deinitialize the list
deInitList(&primes);
puts("Exit successful.");
return exitCode;
}
void printUsage(char *progName) {
printf("%s [[-f <file> [-e <file> | -q]] [-b <base>] [-n <n>] | [-h] | [-v]]\n", progName);
}
void leave() { run = false; }