New C++ header-only library

This commit is contained in:
Daniil Gentili 2021-10-24 18:22:14 +02:00
parent 75f66836d9
commit 420f68e660
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7
16 changed files with 326 additions and 238 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
*.o *.o
*.so *.so
primemodule

103
.vscode/settings.json vendored Normal file
View File

@ -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"
}
}

139
Makefile
View File

@ -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 NAME = primemodule
PHP_CONFIG = php-config
# BINARY = ${NAME}
# Php.ini directories LIBRARY = lib${NAME}.so
#
# 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.
#
COMPILER = g++ COMPILER = g++
LINKER = g++ COMPILER_FLAGS = -Wall -std=c++11 -fpic -finline-functions -ffast-math -O3 -o
#
# 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
#
RM = rm -f RM = rm -f
CP = cp -f CP = cp
MKDIR = mkdir -p MKDIR = mkdir -p
# DESTDIR ?= "/usr"
# 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.
#
SOURCES = $(wildcard *.cpp) all: ${BINARY} ${LIBRARY}
OBJECTS = $(SOURCES:%.cpp=%.o)
# ${BINARY}: src/main.cpp src/${NAME}.hpp
# From here the build instructions start ${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} install: ${BINARY} ${LIBRARY} src/${NAME}.hpp src/${NAME}.h
${LINKER} ${LINKER_FLAGS} -o $@ ${OBJECTS} ${LINKER_DEPENDENCIES} ${MKDIR} ${DESTDIR}/bin ${DESTDIR}/lib ${DESTDIR}/include
${CP} ${BINARY} ${DESTDIR}/bin
${OBJECTS}: ${CP} ${LIBRARY} ${DESTDIR}/lib
${COMPILER} ${COMPILER_FLAGS} $@ ${@:%.o=%.cpp} ${CP} src/primemodule.hpp src/primemodule.h src/primemodule-ffi.h ${DESTDIR}/include
${INI_DIR}:
${MKDIR} ${INI_DIR}
${EXTENSION_DIR}:
${MKDIR} ${EXTENSION_DIR}
install: ${INI_DIR} ${EXTENSION_DIR}
${CP} ${EXTENSION} ${EXTENSION_DIR}
${CP} ${INI} ${INI_DIR}
clean: clean:
${RM} ${EXTENSION} ${OBJECTS} ${RM} ${BINARY} ${LIBRARY}

View File

@ -1,43 +1,55 @@
# PrimeModule-ext # 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
``` Arch Linux (AUR):
sudo apt-get install build-essential php$(echo "<?php echo PHP_MAJOR_VERSION.'.'.PHP_MINOR_VERSION;" | php)-dev && git clone https://github.com/danog/PHP-CPP && cd PHP-CPP && make -j$(nproc) && sudo make install && cd .. && git clone https://github.com/danog/PrimeModule-ext && cd PrimeModule-ext && make -j$(nproc) && sudo make install ```bash
paru -S primemodule
``` ```
API: Debian/Ubuntu:
``` ```
integer factorize( mixed $pq ) sudo apt-get install build-essential && git clone https://github.com/danog/PrimeModule-ext && cd PrimeModule-ext && make -j$(nproc) && sudo make install
``` ```
Parameters: ## API
pq - The number to factorize, a string or an integer. ### Binary
```bash
primemodule number
```
Return value: an integer, containing one of the factors of the number. On success (return code 0), prints to stdout one of the prime factors of `number`.
### C++
Example: ```c++
uint32_t PrimeModule::factorize(uint64_t number);
``` ```
Returns one of the prime factors of `number`, throws an std::runtime_error on failure.
### C
```c
int64_t factorize(uint64_t number);
```
Returns one of the prime factors of `number`, returns -1 on failure.
### PHP
```php
<?php <?php
var_dump(factorize(1724114033281923457));
var_dump(factorize("2189285106422392999")); $f = FFI::load("/usr/include/primemodule-ffi.h");
var_dump(factorize(15));
// Always pass strings to allow 64-bit factorization with no loss of precision on 32-bit PHP.
var_dump($f->factorizeFFI("2189285106422392999"));
``` ```
This will output: Returns one of the prime factors of `2189285106422392999`, returns -1 on failure.
```
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.

View File

@ -1,4 +0,0 @@
<?php
var_dump(factorize(2189285106422392999));
var_dump(factorize("2189285106422392999"));
var_dump(factorize(15));

8
examples/example.c Normal file
View File

@ -0,0 +1,8 @@
#include <primemodule.h>
#include <stdio.h>
#include <stdint.h>
// Compile with -lprimemodule
int main() {
printf("%ld\n", factorize(2189285106422392999));
}

6
examples/example.cpp Normal file
View File

@ -0,0 +1,6 @@
#include <iostream>
#include <primemodule.hpp>
int main() {
std::cout << PrimeModule::factorize(2189285106422392999) << std::endl;
}

6
examples/example.php Normal file
View File

@ -0,0 +1,6 @@
<?php
$f = FFI::load("/usr/include/primemodule-ffi.h");
// Always pass strings to allow 64-bit factorization with no loss of precision on 32-bit PHP.
var_dump($f->factorizeFFI("2189285106422392999"));

3
examples/example.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh -e
primemodule 2189285106422392999

View File

@ -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 <phpcpp.h>
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 &parameters)
{
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>("factorize", {
Php::ByVal("pq")
});
return extension;
}
}

View File

@ -1 +0,0 @@
extension=primemodule.so

32
src/main.cpp Normal file
View File

@ -0,0 +1,32 @@
#include <iostream>
#include <string>
#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;
}

5
src/primemodule-ffi.h Normal file
View File

@ -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);

27
src/primemodule.cpp Normal file
View File

@ -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;
}
}

5
src/primemodule.h Normal file
View File

@ -0,0 +1,5 @@
#pragma once
#include <stdint.h>
// Returns at most a 32-bit unsigned int or -1
extern "C" int64_t factorize(uint64_t number);

77
src/primemodule.hpp Normal file
View File

@ -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 <cstdint>
#include <cstdlib>
#include <stdexcept>
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!");
}
}
}