msieve 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,94 @@
1
+ msieve Ruby gem
2
+ ===============
3
+
4
+ This gem (library) provides Ruby bindings for the msieve library, "A Library for Factoring Large Integers" by Jason Papadopoulos.
5
+
6
+ Usage
7
+ -----
8
+
9
+ It's entirely simple right now, and there is probably a lot to be desired. In any case, use it as follows:
10
+
11
+ <pre><code>$irb -r lib/msieve --simple-prompt
12
+ >> m = Msieve.new("1234567890")
13
+ => #&lt;Msieve:0x14418e0&gt;
14
+ >> m.factor!
15
+ => [2, 3, 3, 5, 3607, 3803]
16
+ >> n = Msieve.new("235711131719")
17
+ => #&lt;Msieve:0x14f28c0&gt;
18
+ >> n.factor!
19
+ => [7, 4363, 7717859]
20
+ >> p = Msieve.new("149162536496481")
21
+ => #&lt;Msieve:0x15b3140&gt;
22
+ >> p.factor!
23
+ => [3, 7549, 48437, 135979]</code></pre>
24
+
25
+ You can also include elementary functions as listed in msieve's readme. I quote:
26
+
27
+ > Starting with v1.08, the inputs to msieve can be integer arithmetic
28
+ > expressions using any of the following operators:
29
+ >
30
+ > * \+ - plus, minus (lowest priority)
31
+ > * % integer remainder
32
+ > * \* / multiply, integer divide
33
+ > * ^ power
34
+ > * ( ) grouping (highest priority)
35
+
36
+ Examples:
37
+
38
+ <pre><code>$irb -r lib\msieve --simple-prompt
39
+ >> m = Msieve.new("(10^17-1)/9")
40
+ => #&lt;Msieve:0x13ea4a0&gt;
41
+ >> m.factor!
42
+ => [2071723, 5363222357]
43
+ >> m = Msieve.new("(10^19-1)/9")
44
+ => #&lt;Msieve:0x1467b80&gt;
45
+ >> m.factor!
46
+ => [1111111111111111111]</code></pre>
47
+
48
+ Caveat
49
+ ------
50
+
51
+ msieve has two headers that have the line
52
+
53
+ <pre><code>#include &lt;util.h&gt;</code></pre>
54
+
55
+ This is no good here, because mkmf always puts Ruby's include paths before the
56
+ ones you request. So it ends up including ruby/util.h. No good! I had to hack
57
+ my copy of msieve, the msieve.h and mp.h files, to change that line to
58
+
59
+ <pre><code>#include "util.h"</code></pre>
60
+
61
+ and boom! Everything works. No word on how to... autotically do this in the
62
+ gem? I am wholly against toying with the global vars that mkmf sets up, in
63
+ order to reorganize the include paths. Don't even suggest that. Yuck.
64
+
65
+
66
+ Bugs
67
+ ----
68
+
69
+ It has some bugs:
70
+
71
+ <pre><code>$irb -r lib\msieve --simple-prompt
72
+
73
+ >> m = Msieve.new(99999999)
74
+ => #&lt;Msieve:0x14e26c0&gt;
75
+ >> m.input
76
+ => "99999999"
77
+ >> m.factor!
78
+ => [3, 3, 11, 73, 101, 137]
79
+ >> m.factor!
80
+ => [3, 3, 11, 73, 101, 137, 3, 3, 11, 73, 101, 137]</code></pre>
81
+
82
+ Todo
83
+ ----
84
+
85
+ * Fix weird crashing bug that occurs sporadically
86
+ * Get all options working that msieve_obj_new() accepts
87
+ * Internal logging???
88
+ * Stop, Restart
89
+ * Integrate with Ruby gmp gem if available (passing in GMP::Z, factors coming
90
+ out as GMP::Z, adding a GMP::Z.factor! method
91
+ * Make Ruby bindings for GMP-ECM, then integrate those, haha.
92
+ * Oh, verify that anything works on !windows.
93
+ * Documentation
94
+ * CHANGELOG, gemify, etc.
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'mkmf'
4
+
5
+ dir_config('gmp')
6
+ dir_config('msieve')
7
+
8
+ ok = true
9
+ unless have_header('gmp.h')
10
+ $stderr.puts "can't find gmp.h, try --with-gmp-include=<path>"
11
+ ok = false
12
+ end
13
+
14
+ unless have_library('gmp', '__gmpz_init')
15
+ $stderr.puts "can't find -lgmp, try --with-gmp-lib=<path>"
16
+ ok = false
17
+ end
18
+
19
+ unless have_library('msieve', 'msieve_obj_new')
20
+ $stderr.puts "can't find -lmsieve, try --with-msieve-lib=<path>"
21
+ ok = false
22
+ end
23
+
24
+ unless have_header('msieve.h')
25
+ $stderr.puts "can't find msieve.h, try --with-msieve-include=<path>"
26
+ ok = false
27
+ end
28
+
29
+ $CFLAGS += ' -Wall -W -O6 -g'
30
+ if ok
31
+ create_makefile('msieve')
32
+ else
33
+ raise "Unable to build, correct above errors and rerun"
34
+ end
@@ -0,0 +1,327 @@
1
+ #include <msieve.h>
2
+ #include <ruby.h>
3
+
4
+ #if !defined(RSTRING_LEN)
5
+ # define RSTRING_LEN(x) (RSTRING(x)->len)
6
+ # define RSTRING_PTR(x) (RSTRING(x)->ptr)
7
+ #endif
8
+
9
+ #define msieve_get_struct(ruby_var,c_var) { Data_Get_Struct(ruby_var, msieve_obj, c_var); }
10
+ #define msieve_make_struct(ruby_var,c_var) { ruby_var = Data_Make_Struct(cMsieve, msieve_obj, 0, 0, c_var); }
11
+ #define BIGNUM_P(value) (TYPE(value) == T_BIGNUM)
12
+ #define HASH_P(value) (TYPE(value) == T_HASH)
13
+ #define NUMERIC_P(value) (FIXNUM_P(value) || BIGNUM_P(value))
14
+ #define STRING_P(value) (TYPE(value) == T_STRING)
15
+ #define MSIEVE_P(value) (rb_obj_is_instance_of(value, cMsieve) == Qtrue)
16
+ #define rb_symbol(value) (rb_funcall(rb_str_new2(value), rb_intern("to_sym"), 0))
17
+
18
+ #define DEFUN_0ARG_UINT32(fname) \
19
+ static VALUE r_msieve_##fname(VALUE self) \
20
+ { \
21
+ msieve_obj *self_val; \
22
+ msieve_get_struct (self, self_val); \
23
+ \
24
+ uint32 attr_val = self_val->fname; \
25
+ return INT2FIX(attr_val); \
26
+ }
27
+
28
+ #define DEFUN_0ARG_UINT64(fname) \
29
+ static VALUE r_msieve_##fname(VALUE self) \
30
+ { \
31
+ msieve_obj *self_val; \
32
+ msieve_get_struct (self, self_val); \
33
+ \
34
+ uint64 attr_val = self_val->fname; \
35
+ return INT2FIX(attr_val); \
36
+ }
37
+
38
+ #define DEFUN_0ARG_CHARSTAR(fname) \
39
+ static VALUE r_msieve_##fname(VALUE self) \
40
+ { \
41
+ msieve_obj *self_val; \
42
+ msieve_get_struct (self, self_val); \
43
+ \
44
+ char *attr_val = self_val->fname; \
45
+ return rb_str_new2(attr_val); \
46
+ }
47
+
48
+ VALUE cMsieve;
49
+ VALUE cMsieve_Factor;
50
+
51
+ void msieve_set_factors(VALUE obj);
52
+
53
+ /*
54
+ * Swiped from msieve's demo for now. Will push to Ruby (and maybe GMP) later.
55
+ */
56
+ void get_random_seeds(uint32 *seed1, uint32 *seed2) {
57
+
58
+ uint32 tmp_seed1, tmp_seed2;
59
+
60
+ /* In a multithreaded program, every msieve object
61
+ should have two unique, non-correlated seeds
62
+ chosen for it */
63
+
64
+ #if !defined(WIN32) && !defined(_WIN64)
65
+
66
+ FILE *rand_device = fopen("/dev/urandom", "r");
67
+
68
+ if (rand_device != NULL) {
69
+
70
+ /* Yay! Cryptographic-quality nondeterministic randomness! */
71
+
72
+ fread(&tmp_seed1, sizeof(uint32), (size_t)1, rand_device);
73
+ fread(&tmp_seed2, sizeof(uint32), (size_t)1, rand_device);
74
+ fclose(rand_device);
75
+ }
76
+ else
77
+
78
+ #endif
79
+ {
80
+ /* <Shrug> For everyone else, sample the current time,
81
+ the high-res timer (hopefully not correlated to the
82
+ current time), and the process ID. Multithreaded
83
+ applications should fold in the thread ID too */
84
+
85
+ uint64 high_res_time = read_clock();
86
+ tmp_seed1 = ((uint32)(high_res_time >> 32) ^
87
+ (uint32)time(NULL)) *
88
+ (uint32)getpid();
89
+ tmp_seed2 = (uint32)high_res_time;
90
+ }
91
+
92
+ /* The final seeds are the result of a multiplicative
93
+ hash of the initial seeds */
94
+
95
+ (*seed1) = tmp_seed1 * ((uint32)40499 * 65543);
96
+ (*seed2) = tmp_seed2 * ((uint32)40499 * 65543);
97
+ }
98
+
99
+ void msieve_free(void *p) {
100
+ msieve_obj_free(p);
101
+ }
102
+
103
+ VALUE msieve_alloc(VALUE klass) {
104
+ msieve_obj *msieve_val;
105
+ VALUE obj;
106
+
107
+ char *integer_val = "0";
108
+ uint32 default_flags = MSIEVE_FLAG_USE_LOGFILE;
109
+ char *savefile_name = NULL;
110
+ char *logfile_name = NULL;
111
+ char *nfs_fbfile_name = NULL;
112
+ uint32 seed1, seed2;
113
+ get_random_seeds(&seed1, &seed2);
114
+ uint32 max_relations = 0;
115
+ uint64 nfs_lower = 0;
116
+ uint64 nfs_upper = 0;
117
+ enum cpu_type cpu = get_cpu_type();
118
+ uint32 cache_size1;
119
+ uint32 cache_size2;
120
+ get_cache_sizes(&cache_size1, &cache_size2);
121
+ uint32 num_threads = 0;
122
+ uint32 mem_mb = 0;
123
+ uint32 which_gpu = 0;
124
+
125
+ msieve_val = msieve_obj_new(integer_val, default_flags,
126
+ savefile_name, logfile_name, nfs_fbfile_name,
127
+ seed1, seed2, max_relations,
128
+ nfs_lower, nfs_upper, cpu,
129
+ cache_size1, cache_size2,
130
+ num_threads, mem_mb, which_gpu);
131
+ obj = Data_Wrap_Struct(klass, 0, msieve_free, msieve_val);
132
+ return obj;
133
+ }
134
+
135
+ // /*
136
+ // * call-seq:
137
+ // * Msieve.new(hash)
138
+ // *
139
+ // * Creates a new Msieve object (state), with certain arguments and variables
140
+ // * set with <i>hash</i>.
141
+ // */
142
+ // VALUE r_msievesg_new(int argc, VALUE *argv, VALUE klass)
143
+ // {
144
+ // msieve_obj *res_val;
145
+ // VALUE res;
146
+ // (void)klass;
147
+
148
+ // if (argc > 2)
149
+ // rb_raise(rb_eArgError, "wrong # of arguments(%d for 0, 1, or 2)", argc);
150
+
151
+ // msieve_make_struct (res, res_val);
152
+ // rb_obj_call_init (res, argc, argv);
153
+
154
+ // return res;
155
+ // }
156
+
157
+ VALUE r_msieve_initialize(int argc, VALUE *argv, VALUE self)
158
+ {
159
+ msieve_obj *self_val;
160
+ VALUE integer, hash, hash_value, str;
161
+ char *integer_val;
162
+
163
+ msieve_get_struct (self, self_val);
164
+ rb_scan_args (argc, argv, "11", &integer, &hash);
165
+ if (NUMERIC_P(integer)) {
166
+ VALUE integer_s = rb_funcall (integer, rb_intern("to_s"), 0);
167
+ str = StringValue (integer_s);
168
+ }
169
+ else if (STRING_P(integer)) {
170
+ str = StringValue (integer);
171
+ }
172
+ else {
173
+ rb_raise (rb_eArgError, "integer must be Numeric or String");
174
+ }
175
+ integer_val = RSTRING_PTR(str);
176
+ self_val->input = integer_val;
177
+
178
+ if (NIL_P(hash)) { hash = rb_hash_new (); }
179
+ if (! HASH_P(hash)) {
180
+ rb_raise (rb_eArgError, "options must be a Hash");
181
+ }
182
+
183
+ hash_value = rb_hash_aref(hash, rb_symbol("quiet"));
184
+ if (hash_value != Qnil) {
185
+ self_val->flags &= ~(MSIEVE_FLAG_USE_LOGFILE | MSIEVE_FLAG_LOG_TO_STDOUT);
186
+ }
187
+
188
+ hash_value = rb_hash_aref(hash, rb_symbol("max_relations"));
189
+ if (hash_value != Qnil) {
190
+ self_val->max_relations = FIX2INT(hash_value);
191
+ }
192
+
193
+ hash_value = rb_hash_aref(hash, rb_symbol("num_threads"));
194
+ if (hash_value != Qnil) {
195
+ self_val->num_threads = FIX2INT(hash_value);
196
+ }
197
+
198
+ hash_value = rb_hash_aref(hash, rb_symbol("mem_mb"));
199
+ if (hash_value != Qnil) {
200
+ self_val->mem_mb = FIX2INT(hash_value);
201
+ }
202
+
203
+ return Qnil;
204
+ }
205
+
206
+ VALUE r_msieve_factor_bang(VALUE self)
207
+ {
208
+ msieve_obj *self_val;
209
+ msieve_get_struct (self, self_val);
210
+
211
+ msieve_run(self_val);
212
+
213
+ if (!(self_val->flags & MSIEVE_FLAG_FACTORIZATION_DONE)) {
214
+ rb_raise (rb_eRuntimeError, "current factorization was interrupted");
215
+ }
216
+
217
+ msieve_set_factors(self);
218
+
219
+ return rb_iv_get (self, "@factors");
220
+ }
221
+
222
+ void msieve_set_factors(VALUE obj) {
223
+ msieve_obj *obj_val;
224
+ msieve_get_struct (obj, obj_val);
225
+ VALUE factors_array = rb_ary_new ();
226
+
227
+ //rb_iv_set (obj, "@factors", rb_ary_new);
228
+
229
+ msieve_factor *factor = obj_val->factors;
230
+ while (factor != NULL) {
231
+ VALUE factor_type;
232
+
233
+ if (factor->factor_type == MSIEVE_PRIME)
234
+ factor_type = rb_symbol("prime");
235
+ else if (factor->factor_type == MSIEVE_COMPOSITE)
236
+ factor_type = rb_symbol("composite");
237
+ else
238
+ factor_type = rb_symbol("probably_prime");
239
+
240
+ VALUE factor_obj = rb_funcall (cMsieve_Factor,
241
+ rb_intern("new"),
242
+ 3,
243
+ factor_type,
244
+ rb_str_new2(factor->number),
245
+ INT2FIX((int32)strlen(factor->number)));
246
+ rb_ary_push(factors_array, factor_obj);
247
+
248
+ factor = factor->next;
249
+ }
250
+ rb_iv_set (obj, "@factors", factors_array);
251
+ }
252
+
253
+ VALUE r_msieve_seed1(VALUE self)
254
+ {
255
+ msieve_obj *self_val;
256
+ msieve_get_struct (self, self_val);
257
+
258
+ long seed1_val = self_val->seed1;
259
+ VALUE seed1 = LONG2FIX(seed1_val);
260
+
261
+ return seed1;
262
+ }
263
+
264
+ DEFUN_0ARG_CHARSTAR(input)
265
+ DEFUN_0ARG_UINT32(seed2)
266
+ DEFUN_0ARG_UINT32(max_relations)
267
+ DEFUN_0ARG_UINT64(nfs_lower)
268
+ DEFUN_0ARG_UINT64(nfs_upper)
269
+ DEFUN_0ARG_UINT32(cache_size1)
270
+ DEFUN_0ARG_UINT32(cache_size2)
271
+ DEFUN_0ARG_UINT32(num_threads)
272
+ DEFUN_0ARG_UINT32(mem_mb)
273
+
274
+ VALUE r_msieve_factor_initialize(int argc, VALUE *argv, VALUE self) {
275
+ VALUE factor_type, number, digits;
276
+
277
+ rb_scan_args (argc, argv, "3", &factor_type, &number, &digits);
278
+ rb_iv_set (self, "@factor_type", factor_type);
279
+ rb_iv_set (self, "@number", number);
280
+ rb_iv_set (self, "@digits", digits);
281
+
282
+ return Qnil;
283
+ }
284
+
285
+ VALUE r_msieve_factor_inspect(VALUE self) {
286
+ return rb_iv_get (self, "@number");
287
+ }
288
+
289
+ VALUE r_msieve_factor_to_i(VALUE self) {
290
+ return rb_funcall(rb_iv_get (self, "@number"), rb_intern("to_i"), 0);
291
+ }
292
+
293
+ VALUE r_msieve_factor_to_s(VALUE self) {
294
+ return rb_iv_get (self, "@number");
295
+ }
296
+
297
+ void Init_msieve() {
298
+ cMsieve = rb_define_class ("Msieve", rb_cObject);
299
+ char msieve_version_string[5];
300
+ sprintf (msieve_version_string, "%d.%02d", MSIEVE_MAJOR_VERSION, MSIEVE_MINOR_VERSION);
301
+ rb_define_const (cMsieve, "MSIEVE_VERSION", rb_str_new2 (msieve_version_string));
302
+ rb_define_const (cMsieve, "VERSION", rb_str_new2 ("0.1.0"));
303
+
304
+ rb_define_alloc_func (cMsieve, msieve_alloc);
305
+ rb_define_method (cMsieve, "initialize", r_msieve_initialize, -1);
306
+ rb_define_method (cMsieve, "factor!", r_msieve_factor_bang, 0);
307
+ rb_define_method (cMsieve, "input", r_msieve_input, 0);
308
+ rb_define_method (cMsieve, "seed1", r_msieve_seed1, 0);
309
+ rb_define_method (cMsieve, "seed2", r_msieve_seed2, 0);
310
+ rb_define_method (cMsieve, "max_relations", r_msieve_max_relations, 0);
311
+ rb_define_method (cMsieve, "nfs_lower", r_msieve_nfs_lower, 0);
312
+ rb_define_method (cMsieve, "nfs_upper", r_msieve_nfs_upper, 0);
313
+ rb_define_method (cMsieve, "cache_size1", r_msieve_cache_size1, 0);
314
+ rb_define_method (cMsieve, "cache_size2", r_msieve_cache_size2, 0);
315
+ rb_define_method (cMsieve, "num_threads", r_msieve_num_threads, 0);
316
+ rb_define_method (cMsieve, "mem_mb", r_msieve_mem_mb, 0);
317
+ rb_define_attr (cMsieve, "factors", 1, 0);
318
+
319
+ cMsieve_Factor = rb_define_class_under (cMsieve, "Factor", rb_cObject);
320
+ rb_define_method (cMsieve_Factor, "initialize", r_msieve_factor_initialize, -1);
321
+ rb_define_method (cMsieve_Factor, "inspect", r_msieve_factor_inspect, 0);
322
+ rb_define_method (cMsieve_Factor, "to_i", r_msieve_factor_to_i, 0);
323
+ rb_define_method (cMsieve_Factor, "to_s", r_msieve_factor_to_s, 0);
324
+ rb_define_attr (cMsieve_Factor, "factor_type", 1, 0);
325
+ rb_define_attr (cMsieve_Factor, "number", 1, 0);
326
+ rb_define_attr (cMsieve_Factor, "digits", 1, 0);
327
+ }
@@ -0,0 +1,18 @@
1
+ # Thank you, Nokogiri
2
+
3
+ require 'rbconfig'
4
+
5
+ ENV['PATH'] = [File.expand_path(
6
+ File.join(File.dirname(__FILE__), "..", "ext")
7
+ ), ENV['PATH']].compact.join(';') if RbConfig::CONFIG['host_os'] =~ /(mswin|mingw|mingw32)/i
8
+
9
+ require File.dirname(__FILE__) + '/../ext/msieve'
10
+
11
+ class Msieve
12
+ def self.clear_log(log_name = "msieve.log")
13
+ if File.exist? log_name
14
+ File.delete log_name
15
+ end
16
+ puts "Cleared! (but not really)."
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ require 'test_helper'
2
+
3
+ class TC_Initiate < Test::Unit::TestCase
4
+ def setup
5
+ @sample_fixnums = [ 1, 2, 3, 4, 5, 8, 9, 15, 16, 17, 31, 32, 33,
6
+ 2**10-1, 2**10, 2*3*4*5*6*7, 10**6, 2**20]
7
+ @sample_strings = [ "1", "2", "3", "4", "5", "8", "9", "15",
8
+ "16", "17", "31", "32", "33", "2**10-1",
9
+ "2**10", "2*3*4*5*6*7", "10**6", "2**20"]
10
+ end
11
+
12
+ def test_initiate
13
+ (@sample_fixnums + @sample_strings).each do |i|
14
+ m = Msieve.new(i)
15
+ assert_equal(i.to_s, m.input, "Msieve.new(#{i.inspect}) should initiate ok.")
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,12 @@
1
+ require 'test_helper'
2
+
3
+ class TC_Version < Test::Unit::TestCase
4
+ def setup
5
+
6
+ end
7
+
8
+ def test_version
9
+ assert_equal("0.1.0", Msieve::VERSION, "msieve gem version should be correct")
10
+ assert_equal("1.44", Msieve::MSIEVE_VERSION, "msieve version should be correct")
11
+ end
12
+ end
@@ -0,0 +1,8 @@
1
+ require 'test/unit'
2
+ require 'rbconfig'
3
+
4
+ ENV['PATH'] = [File.expand_path(
5
+ File.join(File.dirname(__FILE__), "..", "ext")
6
+ ), ENV['PATH']].compact.join(';') if RbConfig::CONFIG['host_os'] =~ /(mswin|mingw|mingw32)/i
7
+
8
+ require File.dirname(__FILE__) + '/../ext/msieve'
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'test_helper'
4
+
5
+ require 'tc_version'
6
+ require 'tc_initiate'
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: msieve
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.1"
5
+ platform: ruby
6
+ authors:
7
+ - Sam Rawlins
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-05-04 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: msieve - providing Ruby bindings to the msieve library.
17
+ email:
18
+ - sam.rawlins@gmail.com
19
+ executables: []
20
+
21
+ extensions:
22
+ - ext/extconf.rb
23
+ extra_rdoc_files: []
24
+
25
+ files:
26
+ - ext/msieve.c
27
+ - ext/extconf.rb
28
+ - lib/msieve.rb
29
+ - test/tc_initiate.rb
30
+ - test/tc_version.rb
31
+ - test/test_helper.rb
32
+ - test/unit_tests.rb
33
+ - README.markdown
34
+ has_rdoc: true
35
+ homepage: http://github.com/srawlins/msieve
36
+ licenses: []
37
+
38
+ post_install_message:
39
+ rdoc_options: []
40
+
41
+ require_paths:
42
+ - lib
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 1.8.6
48
+ version:
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ requirements:
56
+ - msieve compiled and working properly.
57
+ rubyforge_project:
58
+ rubygems_version: 1.3.5
59
+ signing_key:
60
+ specification_version: 3
61
+ summary: Provides Ruby bindings to the msieve library.
62
+ test_files: []
63
+