aboutsummaryrefslogtreecommitdiff
path: root/deduper/libpuzzle/php/libpuzzle
diff options
context:
space:
mode:
Diffstat (limited to 'deduper/libpuzzle/php/libpuzzle')
-rw-r--r--deduper/libpuzzle/php/libpuzzle/CREDITS1
-rw-r--r--deduper/libpuzzle/php/libpuzzle/EXPERIMENTAL0
-rw-r--r--deduper/libpuzzle/php/libpuzzle/LICENSE15
-rw-r--r--deduper/libpuzzle/php/libpuzzle/Makefile.am15
-rw-r--r--deduper/libpuzzle/php/libpuzzle/README4
-rw-r--r--deduper/libpuzzle/php/libpuzzle/build/Makefile.am0
-rw-r--r--deduper/libpuzzle/php/libpuzzle/config.m449
-rw-r--r--deduper/libpuzzle/php/libpuzzle/include/Makefile.am0
-rw-r--r--deduper/libpuzzle/php/libpuzzle/libpuzzle.c410
-rw-r--r--deduper/libpuzzle/php/libpuzzle/libpuzzle.php21
-rw-r--r--deduper/libpuzzle/php/libpuzzle/modules/Makefile.am0
-rw-r--r--deduper/libpuzzle/php/libpuzzle/php_libpuzzle.h66
-rw-r--r--deduper/libpuzzle/php/libpuzzle/tests/001.phpt10
-rw-r--r--deduper/libpuzzle/php/libpuzzle/tests/002.phpt15
-rw-r--r--deduper/libpuzzle/php/libpuzzle/tests/003.phpt24
-rw-r--r--deduper/libpuzzle/php/libpuzzle/tests/Makefile.am7
-rw-r--r--deduper/libpuzzle/php/libpuzzle/tests/pics/Makefile.am3
-rw-r--r--deduper/libpuzzle/php/libpuzzle/tests/pics/pic-a-0.jpgbin0 -> 13946 bytes
-rw-r--r--deduper/libpuzzle/php/libpuzzle/tests/pics/pic-a-1.jpgbin0 -> 27407 bytes
19 files changed, 640 insertions, 0 deletions
diff --git a/deduper/libpuzzle/php/libpuzzle/CREDITS b/deduper/libpuzzle/php/libpuzzle/CREDITS
new file mode 100644
index 0000000..bb6ecb3
--- /dev/null
+++ b/deduper/libpuzzle/php/libpuzzle/CREDITS
@@ -0,0 +1 @@
+Frank DENIS <j at pureftpd.org>
diff --git a/deduper/libpuzzle/php/libpuzzle/EXPERIMENTAL b/deduper/libpuzzle/php/libpuzzle/EXPERIMENTAL
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/deduper/libpuzzle/php/libpuzzle/EXPERIMENTAL
diff --git a/deduper/libpuzzle/php/libpuzzle/LICENSE b/deduper/libpuzzle/php/libpuzzle/LICENSE
new file mode 100644
index 0000000..1ce2d05
--- /dev/null
+++ b/deduper/libpuzzle/php/libpuzzle/LICENSE
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2007-2015 Frank DENIS <j at pureftpd.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
diff --git a/deduper/libpuzzle/php/libpuzzle/Makefile.am b/deduper/libpuzzle/php/libpuzzle/Makefile.am
new file mode 100644
index 0000000..f582035
--- /dev/null
+++ b/deduper/libpuzzle/php/libpuzzle/Makefile.am
@@ -0,0 +1,15 @@
+EXTRA_DIST = \
+ CREDITS \
+ EXPERIMENTAL \
+ LICENSE \
+ README \
+ config.m4 \
+ libpuzzle.c \
+ libpuzzle.php \
+ php_libpuzzle.h
+
+SUBDIRS = \
+ build \
+ include \
+ modules \
+ tests
diff --git a/deduper/libpuzzle/php/libpuzzle/README b/deduper/libpuzzle/php/libpuzzle/README
new file mode 100644
index 0000000..7bb674f
--- /dev/null
+++ b/deduper/libpuzzle/php/libpuzzle/README
@@ -0,0 +1,4 @@
+This is a PHP extension for libpuzzle.
+
+Have a look at the README-PHP file on top of the libpuzzle distribution for
+more info about that extension.
diff --git a/deduper/libpuzzle/php/libpuzzle/build/Makefile.am b/deduper/libpuzzle/php/libpuzzle/build/Makefile.am
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/deduper/libpuzzle/php/libpuzzle/build/Makefile.am
diff --git a/deduper/libpuzzle/php/libpuzzle/config.m4 b/deduper/libpuzzle/php/libpuzzle/config.m4
new file mode 100644
index 0000000..84f954a
--- /dev/null
+++ b/deduper/libpuzzle/php/libpuzzle/config.m4
@@ -0,0 +1,49 @@
+dnl config.m4 for extension libpuzzle
+
+dnl If your extension references something external, use with:
+
+PHP_ARG_WITH(libpuzzle, for libpuzzle support,
+ [ --with-libpuzzle Include libpuzzle support])
+
+if test "$PHP_LIBPUZZLE" != "no"; then
+ for i in $PHP_LIBPUZZLE /usr/local /usr; do
+ if test -x "$i/bin/gdlib-config"; then
+ GDLIB_CONFIG=$i/bin/gdlib-config
+ break
+ fi
+ done
+ GDLIB_LIBS=$($GDLIB_CONFIG --ldflags --libs)
+ GDLIB_INCS=$($GDLIB_CONFIG --cflags)
+
+ PHP_EVAL_LIBLINE($GDLIB_LIBS, LIBPUZZLE_SHARED_LIBADD)
+ PHP_EVAL_INCLINE($GDLIB_INCS)
+
+ SEARCH_PATH="/usr/local /usr" # you might want to change this
+ SEARCH_FOR="/include/puzzle.h" # you most likely want to change this
+ if test -r $PHP_LIBPUZZLE/$SEARCH_FOR; then # path given as parameter
+ LIBPUZZLE_DIR=$PHP_LIBPUZZLE
+ else # search default path list
+ AC_MSG_CHECKING([for libpuzzle files in default path])
+ for i in $SEARCH_PATH ; do
+ if test -r $i/$SEARCH_FOR; then
+ LIBPUZZLE_DIR=$i
+ AC_MSG_RESULT(found in $i)
+ fi
+ done
+ fi
+
+ if test -z "$LIBPUZZLE_DIR"; then
+ AC_MSG_RESULT([not found])
+ AC_MSG_ERROR([Please reinstall the libpuzzle distribution])
+ fi
+
+ dnl # --with-libpuzzle -> add include path
+ PHP_ADD_INCLUDE($LIBPUZZLE_DIR/include)
+
+ PHP_ADD_LIBRARY_WITH_PATH(puzzle, $LIBPUZZLE_DIR/lib,
+ LIBPUZZLE_SHARED_LIBADD)
+
+ PHP_SUBST(LIBPUZZLE_SHARED_LIBADD)
+
+ PHP_NEW_EXTENSION(libpuzzle, libpuzzle.c, $ext_shared)
+fi
diff --git a/deduper/libpuzzle/php/libpuzzle/include/Makefile.am b/deduper/libpuzzle/php/libpuzzle/include/Makefile.am
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/deduper/libpuzzle/php/libpuzzle/include/Makefile.am
diff --git a/deduper/libpuzzle/php/libpuzzle/libpuzzle.c b/deduper/libpuzzle/php/libpuzzle/libpuzzle.c
new file mode 100644
index 0000000..82e84c3
--- /dev/null
+++ b/deduper/libpuzzle/php/libpuzzle/libpuzzle.c
@@ -0,0 +1,410 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "php.h"
+#include "php_ini.h"
+#include "ext/standard/info.h"
+#include <puzzle.h>
+#include "php_libpuzzle.h"
+
+ZEND_DECLARE_MODULE_GLOBALS(libpuzzle)
+
+/* True global resources - no need for thread safety here */
+static int le_libpuzzle;
+
+/* {{{ libpuzzle_functions[]
+ */
+zend_function_entry libpuzzle_functions[] = {
+ PHP_FE(puzzle_set_max_width, NULL)
+ PHP_FE(puzzle_set_max_height, NULL)
+ PHP_FE(puzzle_set_lambdas, NULL)
+ PHP_FE(puzzle_set_noise_cutoff, NULL)
+ PHP_FE(puzzle_set_p_ratio, NULL)
+ PHP_FE(puzzle_set_contrast_barrier_for_cropping, NULL)
+ PHP_FE(puzzle_set_max_cropping_ratio, NULL)
+ PHP_FE(puzzle_set_autocrop, NULL)
+
+ PHP_FE(puzzle_fill_cvec_from_file, NULL)
+ PHP_FE(puzzle_compress_cvec, NULL)
+ PHP_FE(puzzle_uncompress_cvec, NULL)
+ PHP_FE(puzzle_vector_normalized_distance, NULL)
+
+ {NULL, NULL, NULL} /* Must be the last line in libpuzzle_functions[] */
+};
+/* }}} */
+
+/* {{{ libpuzzle_module_entry
+ */
+zend_module_entry libpuzzle_module_entry = {
+#if ZEND_MODULE_API_NO >= 20010901
+ STANDARD_MODULE_HEADER,
+#endif
+ "libpuzzle",
+ libpuzzle_functions,
+ PHP_MINIT(libpuzzle),
+ PHP_MSHUTDOWN(libpuzzle),
+ PHP_RINIT(libpuzzle), /* Replace with NULL if there's nothing to do at request start */
+ PHP_RSHUTDOWN(libpuzzle), /* Replace with NULL if there's nothing to do at request end */
+ PHP_MINFO(libpuzzle),
+#if ZEND_MODULE_API_NO >= 20010901
+ "0.10", /* Replace with version number for your extension */
+#endif
+ STANDARD_MODULE_PROPERTIES
+};
+/* }}} */
+
+#ifdef COMPILE_DL_LIBPUZZLE
+ZEND_GET_MODULE(libpuzzle)
+#endif
+
+
+/* {{{ PHP_MINIT_FUNCTION
+ */
+PHP_MINIT_FUNCTION(libpuzzle)
+{
+ REGISTER_DOUBLE_CONSTANT("PUZZLE_CVEC_SIMILARITY_THRESHOLD",
+ PUZZLE_CVEC_SIMILARITY_THRESHOLD,
+ CONST_CS | CONST_PERSISTENT);
+ REGISTER_DOUBLE_CONSTANT("PUZZLE_CVEC_SIMILARITY_HIGH_THRESHOLD",
+ PUZZLE_CVEC_SIMILARITY_HIGH_THRESHOLD,
+ CONST_CS | CONST_PERSISTENT);
+ REGISTER_DOUBLE_CONSTANT("PUZZLE_CVEC_SIMILARITY_LOW_THRESHOLD",
+ PUZZLE_CVEC_SIMILARITY_LOW_THRESHOLD,
+ CONST_CS | CONST_PERSISTENT);
+ REGISTER_DOUBLE_CONSTANT("PUZZLE_CVEC_SIMILARITY_LOWER_THRESHOLD",
+ PUZZLE_CVEC_SIMILARITY_LOWER_THRESHOLD,
+ CONST_CS | CONST_PERSISTENT);
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_MSHUTDOWN_FUNCTION
+ */
+PHP_MSHUTDOWN_FUNCTION(libpuzzle)
+{
+ return SUCCESS;
+}
+/* }}} */
+
+/* Remove if there's nothing to do at request start */
+/* {{{ PHP_RINIT_FUNCTION
+ */
+PHP_RINIT_FUNCTION(libpuzzle)
+{
+ puzzle_init_context(&LIBPUZZLE_G(global_context));
+ return SUCCESS;
+}
+/* }}} */
+
+/* Remove if there's nothing to do at request end */
+/* {{{ PHP_RSHUTDOWN_FUNCTION
+ */
+PHP_RSHUTDOWN_FUNCTION(libpuzzle)
+{
+ puzzle_free_context(&LIBPUZZLE_G(global_context));
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_MINFO_FUNCTION
+ */
+PHP_MINFO_FUNCTION(libpuzzle)
+{
+ php_info_print_table_start();
+ php_info_print_table_header(2, "libpuzzle support", "enabled");
+ php_info_print_table_end();
+}
+/* }}} */
+
+/* {{{ proto string puzzle_fill_cvec_from_file(string filename)
+ * Creates a signature out of an image file */
+PHP_FUNCTION(puzzle_fill_cvec_from_file)
+{
+ char *arg = NULL;
+ int arg_len;
+ PuzzleContext *context;
+ PuzzleCvec cvec;
+
+ context = &LIBPUZZLE_G(global_context);
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
+ "s", &arg, &arg_len) == FAILURE ||
+ arg_len <= 0) {
+ RETURN_FALSE;
+ }
+ puzzle_init_cvec(context, &cvec);
+ if (puzzle_fill_cvec_from_file(context, &cvec, arg) != 0) {
+ puzzle_free_cvec(context, &cvec);
+ RETURN_FALSE;
+ }
+ RETVAL_STRINGL(cvec.vec, cvec.sizeof_vec, 1);
+ puzzle_free_cvec(context, &cvec);
+}
+/* }}} */
+
+/* {{{ proto string puzzle_compress_cvec(string cvec)
+ * Compress a signature to save storage space */
+PHP_FUNCTION(puzzle_compress_cvec)
+{
+ char *arg = NULL;
+ int arg_len;
+ PuzzleContext *context;
+ PuzzleCompressedCvec compressed_cvec;
+ PuzzleCvec cvec;
+
+ context = &LIBPUZZLE_G(global_context);
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
+ "s", &arg, &arg_len) == FAILURE ||
+ arg_len <= 0) {
+ RETURN_FALSE;
+ }
+ puzzle_init_compressed_cvec(context, &compressed_cvec);
+ puzzle_init_cvec(context, &cvec);
+ cvec.vec = arg;
+ cvec.sizeof_vec = (size_t) arg_len;
+ if (puzzle_compress_cvec(context, &compressed_cvec, &cvec) != 0) {
+ puzzle_free_compressed_cvec(context, &compressed_cvec);
+ cvec.vec = NULL;
+ puzzle_free_cvec(context, &cvec);
+ RETURN_FALSE;
+ }
+ RETVAL_STRINGL(compressed_cvec.vec,
+ compressed_cvec.sizeof_compressed_vec, 1);
+ puzzle_free_compressed_cvec(context, &compressed_cvec);
+ cvec.vec = NULL;
+ puzzle_free_cvec(context, &cvec);
+}
+/* }}} */
+
+/* {{{ proto string puzzle_uncompress_cvec(string compressed_cvec)
+ * Uncompress a compressed signature so that it can be used for computations */
+PHP_FUNCTION(puzzle_uncompress_cvec)
+{
+ char *arg = NULL;
+ int arg_len;
+ PuzzleContext *context;
+ PuzzleCompressedCvec compressed_cvec;
+ PuzzleCvec cvec;
+
+ context = &LIBPUZZLE_G(global_context);
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
+ "s", &arg, &arg_len) == FAILURE ||
+ arg_len <= 0) {
+ RETURN_FALSE;
+ }
+ puzzle_init_compressed_cvec(context, &compressed_cvec);
+ puzzle_init_cvec(context, &cvec);
+ compressed_cvec.vec = arg;
+ compressed_cvec.sizeof_compressed_vec = (size_t) arg_len;
+ if (puzzle_uncompress_cvec(context, &compressed_cvec, &cvec) != 0) {
+ puzzle_free_cvec(context, &cvec);
+ compressed_cvec.vec = NULL;
+ puzzle_free_compressed_cvec(context, &compressed_cvec);
+ RETURN_FALSE;
+ }
+ RETVAL_STRINGL(cvec.vec, cvec.sizeof_vec, 1);
+ puzzle_free_cvec(context, &cvec);
+ compressed_cvec.vec = NULL;
+ puzzle_free_compressed_cvec(context, &compressed_cvec);
+}
+/* }}} */
+
+/* {{{ proto double puzzle_vector_normalized_distance(string cvec1, string cvec2 [, bool fix_for_texts])
+ * Computes the distance between two signatures. Result is between 0.0 and 1.0 */
+PHP_FUNCTION(puzzle_vector_normalized_distance)
+{
+ char *vec1 = NULL, *vec2 = NULL;
+ int vec1_len, vec2_len;
+ PuzzleContext *context;
+ PuzzleCvec cvec1, cvec2;
+ double d;
+ zend_bool fix_for_texts;
+
+ context = &LIBPUZZLE_G(global_context);
+ if (zend_parse_parameters
+ (ZEND_NUM_ARGS() TSRMLS_CC, "ss|b",
+ &vec1, &vec1_len, &vec2, &vec2_len, &fix_for_texts) == FAILURE ||
+ vec1_len <= 0 || vec2_len <= 0) {
+ RETURN_FALSE;
+ }
+ if (ZEND_NUM_ARGS() TSRMLS_CC < 3) {
+ fix_for_texts = (zend_bool) 1;
+ }
+ puzzle_init_cvec(context, &cvec1);
+ puzzle_init_cvec(context, &cvec2);
+ cvec1.vec = vec1;
+ cvec1.sizeof_vec = (size_t) vec1_len;
+ cvec2.vec = vec2;
+ cvec2.sizeof_vec = (size_t) vec2_len;
+ d = puzzle_vector_normalized_distance(context, &cvec1, &cvec2,
+ (int) fix_for_texts);
+ cvec1.vec = cvec2.vec = NULL;
+ puzzle_free_cvec(context, &cvec1);
+ puzzle_free_cvec(context, &cvec2);
+ RETVAL_DOUBLE(d);
+}
+/* }}} */
+
+/* {{{ proto bool puzzle_set_max_width(int width)
+ * Set the maximum picture width */
+PHP_FUNCTION(puzzle_set_max_width)
+{
+ PuzzleContext *context;
+ long width;
+
+ context = &LIBPUZZLE_G(global_context);
+ if (zend_parse_parameters
+ (ZEND_NUM_ARGS() TSRMLS_CC, "l", &width) == FAILURE ||
+ width <= 0L || width > INT_MAX) {
+ RETURN_FALSE;
+ }
+ if (puzzle_set_max_width(context, (unsigned int) width) != 0) {
+ RETURN_FALSE;
+ }
+ RETVAL_TRUE;
+}
+/* }}} */
+
+/* {{{ proto bool puzzle_set_max_height(int height)
+ * Set the maximum picture height */
+PHP_FUNCTION(puzzle_set_max_height)
+{
+ PuzzleContext *context;
+ long height;
+
+ context = &LIBPUZZLE_G(global_context);
+ if (zend_parse_parameters
+ (ZEND_NUM_ARGS() TSRMLS_CC, "l", &height) == FAILURE ||
+ height <= 0L || height > INT_MAX) {
+ RETURN_FALSE;
+ }
+ if (puzzle_set_max_height(context, (unsigned int) height) != 0) {
+ RETURN_FALSE;
+ }
+ RETVAL_TRUE;
+}
+/* }}} */
+
+/* {{{ proto bool puzzle_set_lambdas(int lambdas)
+ * Set the size of the computation grid */
+PHP_FUNCTION(puzzle_set_lambdas)
+{
+ PuzzleContext *context;
+ long lambdas;
+
+ context = &LIBPUZZLE_G(global_context);
+ if (zend_parse_parameters
+ (ZEND_NUM_ARGS() TSRMLS_CC, "l", &lambdas) == FAILURE ||
+ lambdas <= 0L || lambdas > INT_MAX) {
+ RETURN_FALSE;
+ }
+ if (puzzle_set_lambdas(context, (unsigned int) lambdas) != 0) {
+ RETURN_FALSE;
+ }
+ RETVAL_TRUE;
+}
+/* }}} */
+
+/* {{{ proto bool puzzle_set_noise_cutoff(double cutoff)
+ * Set the noise cutoff level */
+PHP_FUNCTION(puzzle_set_noise_cutoff)
+{
+ PuzzleContext *context;
+ double cutoff;
+
+ context = &LIBPUZZLE_G(global_context);
+ if (zend_parse_parameters
+ (ZEND_NUM_ARGS() TSRMLS_CC, "d", &cutoff) == FAILURE) {
+ RETURN_FALSE;
+ }
+ if (puzzle_set_noise_cutoff(context, cutoff) != 0) {
+ RETURN_FALSE;
+ }
+ RETVAL_TRUE;
+}
+/* }}} */
+
+/* {{{ proto bool puzzle_set_p_ratio(double ratio)
+ * Set the p_ratio */
+PHP_FUNCTION(puzzle_set_p_ratio)
+{
+ PuzzleContext *context;
+ double p_ratio;
+
+ context = &LIBPUZZLE_G(global_context);
+ if (zend_parse_parameters
+ (ZEND_NUM_ARGS() TSRMLS_CC, "d", &p_ratio) == FAILURE) {
+ RETURN_FALSE;
+ }
+ if (puzzle_set_p_ratio(context, p_ratio) != 0) {
+ RETURN_FALSE;
+ }
+ RETVAL_TRUE;
+}
+/* }}} */
+
+/* {{{ proto bool puzzle_set_contrast_barrier_for_cropping(double barrier)
+ * Set the tolerance level for cropping */
+PHP_FUNCTION(puzzle_set_contrast_barrier_for_cropping)
+{
+ PuzzleContext *context;
+ double barrier;
+
+ context = &LIBPUZZLE_G(global_context);
+ if (zend_parse_parameters
+ (ZEND_NUM_ARGS() TSRMLS_CC, "d", &barrier) == FAILURE) {
+ RETURN_FALSE;
+ }
+ if (puzzle_set_contrast_barrier_for_cropping(context, barrier) != 0) {
+ RETURN_FALSE;
+ }
+ RETVAL_TRUE;
+}
+/* }}} */
+
+/* {{{ proto bool puzzle_set_max_cropping_ratio(double ratio)
+ * Set the maximum ratio between the cropped area and the whole picture */
+PHP_FUNCTION(puzzle_set_max_cropping_ratio)
+{
+ PuzzleContext *context;
+ double ratio;
+
+ context = &LIBPUZZLE_G(global_context);
+ if (zend_parse_parameters
+ (ZEND_NUM_ARGS() TSRMLS_CC, "d", &ratio) == FAILURE) {
+ RETURN_FALSE;
+ }
+ if (puzzle_set_max_cropping_ratio(context, ratio) != 0) {
+ RETURN_FALSE;
+ }
+ RETVAL_TRUE;
+}
+/* }}} */
+
+/* {{{ proto bool puzzle_set_autocrop(bool autocrop)
+ * TRUE to enable autocropping, FALSE to disable */
+PHP_FUNCTION(puzzle_set_autocrop)
+{
+ PuzzleContext *context;
+ zend_bool autocrop;
+
+ context = &LIBPUZZLE_G(global_context);
+ if (zend_parse_parameters
+ (ZEND_NUM_ARGS() TSRMLS_CC, "b", &autocrop) == FAILURE) {
+ RETURN_FALSE;
+ }
+ if (puzzle_set_autocrop(context, (int) autocrop) != 0) {
+ RETURN_FALSE;
+ }
+ RETVAL_TRUE;
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/deduper/libpuzzle/php/libpuzzle/libpuzzle.php b/deduper/libpuzzle/php/libpuzzle/libpuzzle.php
new file mode 100644
index 0000000..415273b
--- /dev/null
+++ b/deduper/libpuzzle/php/libpuzzle/libpuzzle.php
@@ -0,0 +1,21 @@
+<?php
+$br = (php_sapi_name() == "cli")? "":"<br>";
+
+if(!extension_loaded('libpuzzle')) {
+ dl('libpuzzle.' . PHP_SHLIB_SUFFIX);
+}
+$module = 'libpuzzle';
+$functions = get_extension_funcs($module);
+echo "Functions available in the test extension:$br\n";
+foreach($functions as $func) {
+ echo $func."$br\n";
+}
+echo "$br\n";
+$function = 'confirm_' . $module . '_compiled';
+if (extension_loaded($module)) {
+ $str = $function($module);
+} else {
+ $str = "Module $module is not compiled into PHP";
+}
+echo "$str\n";
+?>
diff --git a/deduper/libpuzzle/php/libpuzzle/modules/Makefile.am b/deduper/libpuzzle/php/libpuzzle/modules/Makefile.am
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/deduper/libpuzzle/php/libpuzzle/modules/Makefile.am
diff --git a/deduper/libpuzzle/php/libpuzzle/php_libpuzzle.h b/deduper/libpuzzle/php/libpuzzle/php_libpuzzle.h
new file mode 100644
index 0000000..1fae819
--- /dev/null
+++ b/deduper/libpuzzle/php/libpuzzle/php_libpuzzle.h
@@ -0,0 +1,66 @@
+#ifndef PHP_LIBPUZZLE_H
+#define PHP_LIBPUZZLE_H
+
+extern zend_module_entry libpuzzle_module_entry;
+#define phpext_libpuzzle_ptr &libpuzzle_module_entry
+
+#ifdef PHP_WIN32
+#define PHP_LIBPUZZLE_API __declspec(dllexport)
+#else
+#define PHP_LIBPUZZLE_API
+#endif
+
+#ifdef ZTS
+#include "TSRM.h"
+#endif
+
+PHP_MINIT_FUNCTION(libpuzzle);
+PHP_MSHUTDOWN_FUNCTION(libpuzzle);
+PHP_RINIT_FUNCTION(libpuzzle);
+PHP_RSHUTDOWN_FUNCTION(libpuzzle);
+PHP_MINFO_FUNCTION(libpuzzle);
+
+PHP_FUNCTION(puzzle_set_max_width);
+PHP_FUNCTION(puzzle_set_max_height);
+PHP_FUNCTION(puzzle_set_lambdas);
+PHP_FUNCTION(puzzle_set_noise_cutoff);
+PHP_FUNCTION(puzzle_set_p_ratio);
+PHP_FUNCTION(puzzle_set_contrast_barrier_for_cropping);
+PHP_FUNCTION(puzzle_set_max_cropping_ratio);
+PHP_FUNCTION(puzzle_set_autocrop);
+
+PHP_FUNCTION(puzzle_fill_cvec_from_file);
+PHP_FUNCTION(puzzle_compress_cvec);
+PHP_FUNCTION(puzzle_uncompress_cvec);
+PHP_FUNCTION(puzzle_vector_normalized_distance);
+
+ZEND_BEGIN_MODULE_GLOBALS(libpuzzle)
+ PuzzleContext global_context;
+ZEND_END_MODULE_GLOBALS(libpuzzle)
+
+/* In every utility function you add that needs to use variables
+ in php_libpuzzle_globals, call TSRMLS_FETCH(); after declaring other
+ variables used by that function, or better yet, pass in TSRMLS_CC
+ after the last function argument and declare your utility function
+ with TSRMLS_DC after the last declared argument. Always refer to
+ the globals in your function as LIBPUZZLE_G(variable). You are
+ encouraged to rename these macros something shorter, see
+ examples in any other php module directory.
+*/
+
+#ifdef ZTS
+#define LIBPUZZLE_G(v) TSRMG(libpuzzle_globals_id, zend_libpuzzle_globals *, v)
+#else
+#define LIBPUZZLE_G(v) (libpuzzle_globals.v)
+#endif
+
+#endif /* PHP_LIBPUZZLE_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/deduper/libpuzzle/php/libpuzzle/tests/001.phpt b/deduper/libpuzzle/php/libpuzzle/tests/001.phpt
new file mode 100644
index 0000000..5a5f5b5
--- /dev/null
+++ b/deduper/libpuzzle/php/libpuzzle/tests/001.phpt
@@ -0,0 +1,10 @@
+--TEST--
+Check for libpuzzle presence
+--SKIPIF--
+<?php if (!extension_loaded("libpuzzle")) print "skip"; ?>
+--FILE--
+<?php
+echo "libpuzzle extension is available";
+?>
+--EXPECT--
+libpuzzle extension is available
diff --git a/deduper/libpuzzle/php/libpuzzle/tests/002.phpt b/deduper/libpuzzle/php/libpuzzle/tests/002.phpt
new file mode 100644
index 0000000..d675145
--- /dev/null
+++ b/deduper/libpuzzle/php/libpuzzle/tests/002.phpt
@@ -0,0 +1,15 @@
+--TEST--
+Check for distance between similar images
+--SKIPIF--
+<?php if (!extension_loaded("libpuzzle")) print "skip"; ?>
+--FILE--
+<?php
+
+$cvec1 = puzzle_fill_cvec_from_file(dirname(__FILE__) . '/pics/pic-a-0.jpg');
+$cvec2 = puzzle_fill_cvec_from_file(dirname(__FILE__) . '/pics/pic-a-1.jpg');
+$d = puzzle_vector_normalized_distance($cvec1, $cvec2);
+exit((int) ($d < PUZZLE_CVEC_SIMILARITY_LOWER_THRESHOLD));
+
+?>
+--EXPECT--
+1
diff --git a/deduper/libpuzzle/php/libpuzzle/tests/003.phpt b/deduper/libpuzzle/php/libpuzzle/tests/003.phpt
new file mode 100644
index 0000000..ba7d5aa
--- /dev/null
+++ b/deduper/libpuzzle/php/libpuzzle/tests/003.phpt
@@ -0,0 +1,24 @@
+--TEST--
+Check the puzzle_set(3) interface
+--SKIPIF--
+<?php if (!extension_loaded("libpuzzle")) print "skip"; ?>
+--FILE--
+<?php
+
+$cvec1 = puzzle_fill_cvec_from_file(dirname(__FILE__) . '/pics/pic-a-0.jpg');
+$cvec2 = puzzle_fill_cvec_from_file(dirname(__FILE__) . '/pics/pic-a-1.jpg');
+puzzle_set_max_width(1500);
+puzzle_set_max_height(1500);
+puzzle_set_lambdas(11);
+puzzle_set_noise_cutoff(1.0);
+puzzle_set_p_ratio(2.0);
+puzzle_set_contrast_barrier_for_cropping(0.1);
+puzzle_set_max_cropping_ratio(0.1);
+puzzle_set_autocrop(FALSE);
+
+$d = puzzle_vector_normalized_distance($cvec1, $cvec2);
+exit((int) ($d < PUZZLE_CVEC_SIMILARITY_LOWER_THRESHOLD));
+
+?>
+--EXPECT--
+1
diff --git a/deduper/libpuzzle/php/libpuzzle/tests/Makefile.am b/deduper/libpuzzle/php/libpuzzle/tests/Makefile.am
new file mode 100644
index 0000000..14ded39
--- /dev/null
+++ b/deduper/libpuzzle/php/libpuzzle/tests/Makefile.am
@@ -0,0 +1,7 @@
+EXTRA_DIST = \
+ 001.phpt \
+ 002.phpt \
+ 003.phpt
+
+SUBDIRS = \
+ pics
diff --git a/deduper/libpuzzle/php/libpuzzle/tests/pics/Makefile.am b/deduper/libpuzzle/php/libpuzzle/tests/pics/Makefile.am
new file mode 100644
index 0000000..0aacd9a
--- /dev/null
+++ b/deduper/libpuzzle/php/libpuzzle/tests/pics/Makefile.am
@@ -0,0 +1,3 @@
+EXTRA_DIST = \
+ pic-a-0.jpg \
+ pic-a-1.jpg
diff --git a/deduper/libpuzzle/php/libpuzzle/tests/pics/pic-a-0.jpg b/deduper/libpuzzle/php/libpuzzle/tests/pics/pic-a-0.jpg
new file mode 100644
index 0000000..3dd4a3b
--- /dev/null
+++ b/deduper/libpuzzle/php/libpuzzle/tests/pics/pic-a-0.jpg
Binary files differ
diff --git a/deduper/libpuzzle/php/libpuzzle/tests/pics/pic-a-1.jpg b/deduper/libpuzzle/php/libpuzzle/tests/pics/pic-a-1.jpg
new file mode 100644
index 0000000..95f0e77
--- /dev/null
+++ b/deduper/libpuzzle/php/libpuzzle/tests/pics/pic-a-1.jpg
Binary files differ