From 420f68e66054de8695b3c3a8feb44f379d12de6b Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Sun, 24 Oct 2021 18:22:14 +0200 Subject: [PATCH] New C++ header-only library --- .gitignore | 1 + .vscode/settings.json | 103 +++++++++++++++++++++++++++++++ Makefile | 139 +++++------------------------------------- README.md | 62 +++++++++++-------- example.php | 4 -- examples/example.c | 8 +++ examples/example.cpp | 6 ++ examples/example.php | 6 ++ examples/example.sh | 3 + main.cpp | 85 -------------------------- primemodule.ini | 1 - src/main.cpp | 32 ++++++++++ src/primemodule-ffi.h | 5 ++ src/primemodule.cpp | 27 ++++++++ src/primemodule.h | 5 ++ src/primemodule.hpp | 77 +++++++++++++++++++++++ 16 files changed, 326 insertions(+), 238 deletions(-) create mode 100644 .vscode/settings.json delete mode 100644 example.php create mode 100644 examples/example.c create mode 100644 examples/example.cpp create mode 100644 examples/example.php create mode 100755 examples/example.sh delete mode 100644 main.cpp delete mode 100644 primemodule.ini create mode 100644 src/main.cpp create mode 100644 src/primemodule-ffi.h create mode 100644 src/primemodule.cpp create mode 100644 src/primemodule.h create mode 100644 src/primemodule.hpp diff --git a/.gitignore b/.gitignore index 9d22eb4..92c0236 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *.o *.so +primemodule diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..1940a4d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,103 @@ +{ + "files.associations": { + "optional": "cpp", + "variant": "cpp", + "cstdint": "cpp", + "__debug": "cpp", + "any": "cpp", + "exception": "cpp", + "functional": "cpp", + "memory": "cpp", + "new": "cpp", + "stdexcept": "cpp", + "typeinfo": "cpp", + "future": "cpp", + "__bit_reference": "cpp", + "__config": "cpp", + "__errc": "cpp", + "__functional_base": "cpp", + "__hash_table": "cpp", + "__locale": "cpp", + "__mutex_base": "cpp", + "__node_handle": "cpp", + "__nullptr": "cpp", + "__split_buffer": "cpp", + "__string": "cpp", + "__threading_support": "cpp", + "__tree": "cpp", + "__tuple": "cpp", + "algorithm": "cpp", + "array": "cpp", + "atomic": "cpp", + "bit": "cpp", + "bitset": "cpp", + "cctype": "cpp", + "chrono": "cpp", + "cinttypes": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "codecvt": "cpp", + "compare": "cpp", + "complex": "cpp", + "concepts": "cpp", + "condition_variable": "cpp", + "csignal": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "forward_list": "cpp", + "fstream": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "ios": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "iterator": "cpp", + "limits": "cpp", + "list": "cpp", + "locale": "cpp", + "map": "cpp", + "mutex": "cpp", + "numeric": "cpp", + "ostream": "cpp", + "queue": "cpp", + "random": "cpp", + "ratio": "cpp", + "regex": "cpp", + "semaphore": "cpp", + "set": "cpp", + "shared_mutex": "cpp", + "sstream": "cpp", + "stack": "cpp", + "streambuf": "cpp", + "string": "cpp", + "string_view": "cpp", + "strstream": "cpp", + "system_error": "cpp", + "thread": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "typeindex": "cpp", + "unordered_map": "cpp", + "unordered_set": "cpp", + "utility": "cpp", + "valarray": "cpp", + "vector": "cpp", + "hash_map": "cpp", + "*.tcc": "cpp", + "memory_resource": "cpp", + "source_location": "cpp", + "numbers": "cpp", + "stop_token": "cpp", + "cfenv": "cpp", + "primemodule-ffi.h": "c", + "primemodule.h": "c" + } +} \ No newline at end of file diff --git a/Makefile b/Makefile index 295cd5e..82855ee 100644 --- a/Makefile +++ b/Makefile @@ -1,138 +1,31 @@ -# -# Makefile template -# -# This is an example Makefile that can be used by anyone who is building -# his or her own PHP extensions using the PHP-CPP library. -# -# In the top part of this file we have included variables that can be -# altered to fit your configuration, near the bottom the instructions and -# dependencies for the compiler are defined. The deeper you get into this -# file, the less likely it is that you will have to change anything in it. -# - -# -# Name of your extension -# -# This is the name of your extension. Based on this extension name, the -# name of the library file (name.so) and the name of the config file (name.ini) -# are automatically generated -# - NAME = primemodule -PHP_CONFIG = php-config -# -# Php.ini directories -# -# In the past, PHP used a single php.ini configuration file. Today, most -# PHP installations use a conf.d directory that holds a set of config files, -# one for each extension. Use this variable to specify this directory. -# -# In Ubuntu 14.04 Apache 2.4 is used, which uses the mods-available directory -# instead of a conf.d directory. In 16.04 the directory changed yet again. -# This has to be checked. -# - -INI_DIR = $(shell php --ini | sed '/Scan for additional .ini files in: /!d;s/Scan for additional .ini files in: //') - -# -# The extension dirs -# -# This is normally a directory like /usr/lib/php5/20121221 (based on the -# PHP version that you use. We make use of the command line 'php-config' -# instruction to find out what the extension directory is, you can override -# this with a different fixed directory -# - -EXTENSION_DIR = `${PHP_CONFIG} --extension-dir` - -# -# The name of the extension and the name of the .ini file -# -# These two variables are based on the name of the extension. We simply add -# a certain extension to them (.so or .ini) -# - -EXTENSION = ${NAME}.so -INI = ${NAME}.ini - -# -# Compiler -# -# By default, the GNU C++ compiler is used. If you want to use a different -# compiler, you can change that here. You can change this for both the -# compiler (the program that turns the c++ files into object files) and for -# the linker (the program that links all object files into the single .so -# library file. By default, g++ (the GNU C++ compiler) is used for both. -# +BINARY = ${NAME} +LIBRARY = lib${NAME}.so COMPILER = g++ -LINKER = g++ - -# -# Compiler and linker flags -# -# This variable holds the flags that are passed to the compiler. By default, -# we include the -O2 flag. This flag tells the compiler to optimize the code, -# but it makes debugging more difficult. So if you're debugging your application, -# you probably want to remove this -O2 flag. At the same time, you can then -# add the -g flag to instruct the compiler to include debug information in -# the library (but this will make the final libphpcpp.so file much bigger, so -# you want to leave that flag out on production servers). -# -# If your extension depends on other libraries (and it does at least depend on -# one: the PHP-CPP library), you should update the LINKER_DEPENDENCIES variable -# with a list of all flags that should be passed to the linker. -# - -COMPILER_FLAGS = -Wall -c -std=c++11 -fpic -finline-functions -ffast-math -O3 -o -LINKER_FLAGS = -shared -LINKER_DEPENDENCIES = -lphpcpp - -# -# Command to remove files, copy files and create directories. -# -# I've never encountered a *nix environment in which these commands do not work. -# So you can probably leave this as it is -# +COMPILER_FLAGS = -Wall -std=c++11 -fpic -finline-functions -ffast-math -O3 -o RM = rm -f -CP = cp -f +CP = cp MKDIR = mkdir -p -# -# All source files are simply all *.cpp files found in the current directory -# -# A built-in Makefile macro is used to scan the current directory and find -# all source files. The object files are all compiled versions of the source -# file, with the .cpp extension being replaced by .o. -# +DESTDIR ?= "/usr" -SOURCES = $(wildcard *.cpp) -OBJECTS = $(SOURCES:%.cpp=%.o) +all: ${BINARY} ${LIBRARY} -# -# From here the build instructions start -# +${BINARY}: src/main.cpp src/${NAME}.hpp + ${COMPILER} ${COMPILER_FLAGS} $@ src/main.cpp -all: ${OBJECTS} ${EXTENSION} +${LIBRARY}: src/${NAME}.cpp src/${NAME}.hpp + ${COMPILER} -shared ${COMPILER_FLAGS} $@ src/${NAME}.cpp -${EXTENSION}: ${OBJECTS} - ${LINKER} ${LINKER_FLAGS} -o $@ ${OBJECTS} ${LINKER_DEPENDENCIES} - -${OBJECTS}: - ${COMPILER} ${COMPILER_FLAGS} $@ ${@:%.o=%.cpp} - -${INI_DIR}: - ${MKDIR} ${INI_DIR} - -${EXTENSION_DIR}: - ${MKDIR} ${EXTENSION_DIR} - -install: ${INI_DIR} ${EXTENSION_DIR} - ${CP} ${EXTENSION} ${EXTENSION_DIR} - ${CP} ${INI} ${INI_DIR} +install: ${BINARY} ${LIBRARY} src/${NAME}.hpp src/${NAME}.h + ${MKDIR} ${DESTDIR}/bin ${DESTDIR}/lib ${DESTDIR}/include + ${CP} ${BINARY} ${DESTDIR}/bin + ${CP} ${LIBRARY} ${DESTDIR}/lib + ${CP} src/primemodule.hpp src/primemodule.h src/primemodule-ffi.h ${DESTDIR}/include clean: - ${RM} ${EXTENSION} ${OBJECTS} + ${RM} ${BINARY} ${LIBRARY} diff --git a/README.md b/README.md index db59f04..ab27f2e 100644 --- a/README.md +++ b/README.md @@ -1,43 +1,55 @@ # PrimeModule-ext -PHP extension for factorizing huge (up to 2^63-1) numbers (optimized for huge semiprimes). +C++ header-only library, binary and FFI library for factorizing huge (up to 2^63-1) numbers (optimized for huge semiprimes). -To compile it, simply install https://github.com/CopernicaMarketingSoftware/PHP-CPP and run `make && sudo make install` in this directory. +## Install -``` -sudo apt-get install build-essential php$(echo "factorizeFFI("2189285106422392999")); ``` -This will output: - -``` -int(1402015859) -int(1117663223) -int(5) -``` - -As you can see, the factorize function accepts integers and strings as parameter, so that if you're poor and you have only a 32 bit system, you will still be able to provide 64 bit integers as a string. - -The function can throw an \Exception if factorization fails. +Returns one of the prime factors of `2189285106422392999`, returns -1 on failure. diff --git a/example.php b/example.php deleted file mode 100644 index 50fb84f..0000000 --- a/example.php +++ /dev/null @@ -1,4 +0,0 @@ - +#include +#include + +// Compile with -lprimemodule +int main() { + printf("%ld\n", factorize(2189285106422392999)); +} \ No newline at end of file diff --git a/examples/example.cpp b/examples/example.cpp new file mode 100644 index 0000000..86395b5 --- /dev/null +++ b/examples/example.cpp @@ -0,0 +1,6 @@ +#include +#include + +int main() { + std::cout << PrimeModule::factorize(2189285106422392999) << std::endl; +} \ No newline at end of file diff --git a/examples/example.php b/examples/example.php new file mode 100644 index 0000000..5ad1c61 --- /dev/null +++ b/examples/example.php @@ -0,0 +1,6 @@ +factorizeFFI("2189285106422392999")); diff --git a/examples/example.sh b/examples/example.sh new file mode 100755 index 0000000..5f273b8 --- /dev/null +++ b/examples/example.sh @@ -0,0 +1,3 @@ +#!/bin/sh -e + +primemodule 2189285106422392999 \ No newline at end of file diff --git a/main.cpp b/main.cpp deleted file mode 100644 index bdcdb87..0000000 --- a/main.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* - * This taken from the source code of tgnet library v. 1.0 - * It is licensed under GNU GPL v. 2 or later. - * You should have received a copy of the license in this archive (see LICENSE). - * - * Copyright Nikolai Kudashov, 2015. - */ - -#include - - -inline uint64_t gcd(uint64_t a, uint64_t b) { - while (a != 0 && b != 0) { - while ((b & 1) == 0) { - b >>= 1; - } - while ((a & 1) == 0) { - a >>= 1; - } - if (a > b) { - a -= b; - } else { - b -= a; - } - } - return b == 0 ? a : b; -} -inline Php::Value factorize(Php::Parameters ¶meters) -{ - uint64_t what = (int64_t) parameters[0]; - int32_t it = 0, i, j; - uint64_t g = 0; - for (i = 0; i < 3 || it < 1000; i++) { - uint64_t t = ((lrand48() & 15) + 17) % what; - uint64_t x = (long long) lrand48() % (what - 1) + 1, y = x; - int32_t lim = 1 << (i + 18); - for (j = 1; j < lim; j++) { - ++it; - uint64_t a = x, b = x, c = t; - while (b) { - if (b & 1) { - c += a; - if (c >= what) { - c -= what; - } - } - a += a; - if (a >= what) { - a -= what; - } - b >>= 1; - } - x = c; - uint64_t z = x < y ? what + x - y : x - y; - g = gcd(z, what); - if (g != 1) { - break; - } - if (!(j & (j - 1))) { - y = x; - } - } - if (g > 1 && g < what) { - break; - } - } - - if (g > 1 && g < what) { - return (int32_t) g; - } else { - throw Php::Exception("Factorization failed!"); - return false; - } -} - -extern "C" { - PHPCPP_EXPORT void *get_module() { - static Php::Extension extension("primemodule", "1.0"); - extension.add("factorize", { - Php::ByVal("pq") - }); - return extension; - } -} - diff --git a/primemodule.ini b/primemodule.ini deleted file mode 100644 index e3d5d0a..0000000 --- a/primemodule.ini +++ /dev/null @@ -1 +0,0 @@ -extension=primemodule.so diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..fd4cbf6 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,32 @@ +#include +#include +#include "primemodule.hpp" + +int main(int argc, char *argv[]) { + if (argc != 2) { + std::cerr << "PrimeModule: extremely fast 64-bit factorization module." << std::endl + << "Copyright (C) 2015-2021 Nikolai Kudashov, Daniil Gentili" << std::endl + << "Licensed under GPLv2" << std::endl + << "https://github.com/danog/PrimeModule-ext" << std::endl + << std::endl << "Usage: " << argv[0] << " number" << std::endl + << std::endl << "One of the prime factors will be printed to stdout." << std::endl + << std::endl; + return 1; + } + uint64_t what; + try { + what = std::stoull(argv[1]); + } catch (...) { + std::cerr << "An invalid 64-bit integer was provided!" << std::endl; + return 1; + } + uint32_t res; + try { + res = PrimeModule::factorize(what); + } catch (...) { + std::cerr << "An error occurred during factorization!" << std::endl; + return 1; + } + std::cout << res << std::endl; + return 0; +} \ No newline at end of file diff --git a/src/primemodule-ffi.h b/src/primemodule-ffi.h new file mode 100644 index 0000000..35aad13 --- /dev/null +++ b/src/primemodule-ffi.h @@ -0,0 +1,5 @@ +#define FFI_SCOPE "primemodule" +#define FFI_LIB "libprimemodule.so" + +// Returns at most a 32-bit signed int or -1 +int32_t factorizeFFI(const char *number); diff --git a/src/primemodule.cpp b/src/primemodule.cpp new file mode 100644 index 0000000..6368f09 --- /dev/null +++ b/src/primemodule.cpp @@ -0,0 +1,27 @@ +/* + * This taken from the source code of tgnet library v. 1.0 + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2015. + */ + +#include "primemodule.h" +#include "primemodule.hpp" + +extern "C" int64_t factorize(uint64_t number) +{ + try { + return PrimeModule::factorize(number); + } catch (...) { + return -1; + } +} +extern "C" int32_t factorizeFFI(const char *number) +{ + try { + return PrimeModule::factorize(std::stoull(number)); + } catch (...) { + return -1; + } +} \ No newline at end of file diff --git a/src/primemodule.h b/src/primemodule.h new file mode 100644 index 0000000..ebe48a7 --- /dev/null +++ b/src/primemodule.h @@ -0,0 +1,5 @@ +#pragma once +#include + +// Returns at most a 32-bit unsigned int or -1 +extern "C" int64_t factorize(uint64_t number); diff --git a/src/primemodule.hpp b/src/primemodule.hpp new file mode 100644 index 0000000..b479e48 --- /dev/null +++ b/src/primemodule.hpp @@ -0,0 +1,77 @@ +#pragma once +/* + * This taken from the source code of tgnet library v. 1.0 + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2015. + */ + +#include +#include +#include + +namespace PrimeModule { + inline uint64_t gcd(uint64_t a, uint64_t b) { + while (a != 0 && b != 0) { + while ((b & 1) == 0) { + b >>= 1; + } + while ((a & 1) == 0) { + a >>= 1; + } + if (a > b) { + a -= b; + } else { + b -= a; + } + } + return b == 0 ? a : b; + } + + // Factorize the provided semiprime, throws an std::runtime_error on failure + uint32_t factorize(uint64_t number) { + int32_t it = 0, i, j; + uint64_t g = 0; + for (i = 0; i < 3 || it < 1000; i++) { + uint64_t t = ((lrand48() & 15) + 17) % number; + uint64_t x = (long long)lrand48() % (number - 1) + 1, y = x; + int32_t lim = 1 << (i + 18); + for (j = 1; j < lim; j++) { + ++it; + uint64_t a = x, b = x, c = t; + while (b) { + if (b & 1) { + c += a; + if (c >= number) { + c -= number; + } + } + a += a; + if (a >= number) { + a -= number; + } + b >>= 1; + } + x = c; + uint64_t z = x < y ? number + x - y : x - y; + g = PrimeModule::gcd(z, number); + if (g != 1) { + break; + } + if (!(j & (j - 1))) { + y = x; + } + } + if (g > 1 && g < number) { + break; + } + } + + if (g > 1 && g < number) { + return (uint32_t)g; + } else { + throw std::runtime_error("Factorization failed!"); + } + } +} \ No newline at end of file