miniball_ruby 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/ext/miniball_ruby/Makefile +157 -0
- data/ext/miniball_ruby/extconf.rb +5 -0
- data/ext/miniball_ruby/miniball_builder_dynamic_d.c +188 -0
- data/ext/miniball_ruby/miniball_builder_dynamic_d.h +43 -0
- data/ext/miniball_ruby/miniball_builder_dynamic_d.o +0 -0
- data/ext/miniball_ruby/miniball_dynamic_d.c +207 -0
- data/ext/miniball_ruby/miniball_dynamic_d.h +43 -0
- data/ext/miniball_ruby/miniball_dynamic_d.o +0 -0
- data/ext/miniball_ruby/miniball_ruby.c +169 -0
- data/ext/miniball_ruby/miniball_ruby.o +0 -0
- data/ext/miniball_ruby/miniball_ruby.so +0 -0
- data/ext/miniball_ruby/point_dynamic_d.c +47 -0
- data/ext/miniball_ruby/point_dynamic_d.h +24 -0
- data/ext/miniball_ruby/point_dynamic_d.o +0 -0
- data/ext/miniball_ruby/util.c +6 -0
- data/ext/miniball_ruby/util.h +7 -0
- data/ext/miniball_ruby/util.o +0 -0
- data/lib/miniball.rb +1 -0
- data/lib/miniball_ruby/miniball.rb +48 -0
- data/lib/miniball_ruby/version.rb +3 -0
- metadata +99 -0
@@ -0,0 +1,157 @@
|
|
1
|
+
|
2
|
+
SHELL = /bin/sh
|
3
|
+
|
4
|
+
#### Start of system configuration section. ####
|
5
|
+
|
6
|
+
srcdir = .
|
7
|
+
topdir = /usr/lib/ruby/1.8/i686-linux
|
8
|
+
hdrdir = $(topdir)
|
9
|
+
VPATH = $(srcdir):$(topdir):$(hdrdir)
|
10
|
+
exec_prefix = $(prefix)
|
11
|
+
prefix = $(DESTDIR)/usr
|
12
|
+
sharedstatedir = $(prefix)/com
|
13
|
+
mandir = $(prefix)/share/man
|
14
|
+
psdir = $(docdir)
|
15
|
+
oldincludedir = $(DESTDIR)/usr/include
|
16
|
+
localedir = $(datarootdir)/locale
|
17
|
+
bindir = $(exec_prefix)/bin
|
18
|
+
libexecdir = $(prefix)/lib/ruby1.8
|
19
|
+
sitedir = $(DESTDIR)/usr/local/lib/site_ruby
|
20
|
+
htmldir = $(docdir)
|
21
|
+
vendorarchdir = $(vendorlibdir)/$(sitearch)
|
22
|
+
includedir = $(prefix)/include
|
23
|
+
infodir = $(prefix)/share/info
|
24
|
+
vendorlibdir = $(vendordir)/$(ruby_version)
|
25
|
+
sysconfdir = $(DESTDIR)/etc
|
26
|
+
libdir = $(exec_prefix)/lib
|
27
|
+
sbindir = $(exec_prefix)/sbin
|
28
|
+
rubylibdir = $(libdir)/ruby/$(ruby_version)
|
29
|
+
docdir = $(datarootdir)/doc/$(PACKAGE)
|
30
|
+
dvidir = $(docdir)
|
31
|
+
vendordir = $(libdir)/ruby/vendor_ruby
|
32
|
+
datarootdir = $(prefix)/share
|
33
|
+
pdfdir = $(docdir)
|
34
|
+
archdir = $(rubylibdir)/$(arch)
|
35
|
+
sitearchdir = $(sitelibdir)/$(sitearch)
|
36
|
+
datadir = $(datarootdir)
|
37
|
+
localstatedir = $(DESTDIR)/var
|
38
|
+
sitelibdir = $(sitedir)/$(ruby_version)
|
39
|
+
|
40
|
+
CC = gcc
|
41
|
+
LIBRUBY = $(LIBRUBY_SO)
|
42
|
+
LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a
|
43
|
+
LIBRUBYARG_SHARED = -l$(RUBY_SO_NAME)
|
44
|
+
LIBRUBYARG_STATIC = -l$(RUBY_SO_NAME)-static
|
45
|
+
|
46
|
+
RUBY_EXTCONF_H =
|
47
|
+
CFLAGS = -fPIC -fno-strict-aliasing -g -g -O2 -fPIC $(cflags)
|
48
|
+
INCFLAGS = -I. -I$(topdir) -I$(hdrdir) -I$(srcdir)
|
49
|
+
DEFS = -D_FILE_OFFSET_BITS=64
|
50
|
+
CPPFLAGS = $(DEFS) $(cppflags)
|
51
|
+
CXXFLAGS = $(CFLAGS)
|
52
|
+
ldflags = -L. -Wl,-Bsymbolic-functions -rdynamic -Wl,-export-dynamic
|
53
|
+
dldflags =
|
54
|
+
archflag =
|
55
|
+
DLDFLAGS = $(ldflags) $(dldflags) $(archflag)
|
56
|
+
LDSHARED = $(CC) -shared
|
57
|
+
AR = ar
|
58
|
+
EXEEXT =
|
59
|
+
|
60
|
+
RUBY_INSTALL_NAME = ruby1.8
|
61
|
+
RUBY_SO_NAME = ruby1.8
|
62
|
+
arch = i686-linux
|
63
|
+
sitearch = i686-linux
|
64
|
+
ruby_version = 1.8
|
65
|
+
ruby = /usr/bin/ruby1.8
|
66
|
+
RUBY = $(ruby)
|
67
|
+
RM = rm -f
|
68
|
+
MAKEDIRS = mkdir -p
|
69
|
+
INSTALL = /usr/bin/install -c
|
70
|
+
INSTALL_PROG = $(INSTALL) -m 0755
|
71
|
+
INSTALL_DATA = $(INSTALL) -m 644
|
72
|
+
COPY = cp
|
73
|
+
|
74
|
+
#### End of system configuration section. ####
|
75
|
+
|
76
|
+
preload =
|
77
|
+
|
78
|
+
libpath = . $(libdir)
|
79
|
+
LIBPATH = -L. -L$(libdir)
|
80
|
+
DEFFILE =
|
81
|
+
|
82
|
+
CLEANFILES = mkmf.log
|
83
|
+
DISTCLEANFILES =
|
84
|
+
|
85
|
+
extout =
|
86
|
+
extout_prefix =
|
87
|
+
target_prefix = /miniball_ruby
|
88
|
+
LOCAL_LIBS =
|
89
|
+
LIBS = $(LIBRUBYARG_SHARED) -lpthread -lrt -ldl -lcrypt -lm -lc
|
90
|
+
SRCS = util.c miniball_builder_dynamic_d.c point_dynamic_d.c miniball_dynamic_d.c miniball_ruby.c
|
91
|
+
OBJS = util.o miniball_builder_dynamic_d.o point_dynamic_d.o miniball_dynamic_d.o miniball_ruby.o
|
92
|
+
TARGET = miniball_ruby
|
93
|
+
DLLIB = $(TARGET).so
|
94
|
+
EXTSTATIC =
|
95
|
+
STATIC_LIB =
|
96
|
+
|
97
|
+
BINDIR = $(bindir)
|
98
|
+
RUBYCOMMONDIR = $(sitedir)$(target_prefix)
|
99
|
+
RUBYLIBDIR = $(sitelibdir)$(target_prefix)
|
100
|
+
RUBYARCHDIR = $(sitearchdir)$(target_prefix)
|
101
|
+
|
102
|
+
TARGET_SO = $(DLLIB)
|
103
|
+
CLEANLIBS = $(TARGET).so $(TARGET).il? $(TARGET).tds $(TARGET).map
|
104
|
+
CLEANOBJS = *.o *.a *.s[ol] *.pdb *.exp *.bak
|
105
|
+
|
106
|
+
all: $(DLLIB)
|
107
|
+
static: $(STATIC_LIB)
|
108
|
+
|
109
|
+
clean:
|
110
|
+
@-$(RM) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES)
|
111
|
+
|
112
|
+
distclean: clean
|
113
|
+
@-$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log
|
114
|
+
@-$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES)
|
115
|
+
|
116
|
+
realclean: distclean
|
117
|
+
install: install-so install-rb
|
118
|
+
|
119
|
+
install-so: $(RUBYARCHDIR)
|
120
|
+
install-so: $(RUBYARCHDIR)/$(DLLIB)
|
121
|
+
$(RUBYARCHDIR)/$(DLLIB): $(DLLIB)
|
122
|
+
$(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR)
|
123
|
+
install-rb: pre-install-rb install-rb-default
|
124
|
+
install-rb-default: pre-install-rb-default
|
125
|
+
pre-install-rb: Makefile
|
126
|
+
pre-install-rb-default: Makefile
|
127
|
+
$(RUBYARCHDIR):
|
128
|
+
$(MAKEDIRS) $@
|
129
|
+
|
130
|
+
site-install: site-install-so site-install-rb
|
131
|
+
site-install-so: install-so
|
132
|
+
site-install-rb: install-rb
|
133
|
+
|
134
|
+
.SUFFIXES: .c .m .cc .cxx .cpp .C .o
|
135
|
+
|
136
|
+
.cc.o:
|
137
|
+
$(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $<
|
138
|
+
|
139
|
+
.cxx.o:
|
140
|
+
$(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $<
|
141
|
+
|
142
|
+
.cpp.o:
|
143
|
+
$(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $<
|
144
|
+
|
145
|
+
.C.o:
|
146
|
+
$(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $<
|
147
|
+
|
148
|
+
.c.o:
|
149
|
+
$(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) -c $<
|
150
|
+
|
151
|
+
$(DLLIB): $(OBJS) Makefile
|
152
|
+
@-$(RM) $@
|
153
|
+
$(LDSHARED) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS)
|
154
|
+
|
155
|
+
|
156
|
+
|
157
|
+
$(OBJS): ruby.h defines.h
|
@@ -0,0 +1,188 @@
|
|
1
|
+
#include <stdlib.h>
|
2
|
+
#include "util.h"
|
3
|
+
#include "point_dynamic_d.h"
|
4
|
+
#include "miniball_builder_dynamic_d.h"
|
5
|
+
|
6
|
+
MiniballBuilder* MiniballBuilder_create(int dim) {
|
7
|
+
int size = sizeof(double) * (dim+1);
|
8
|
+
int innerSize = sizeof(double) * dim;
|
9
|
+
|
10
|
+
MiniballBuilder* mb = malloc(sizeof(MiniballBuilder));
|
11
|
+
|
12
|
+
mb->d = dim;
|
13
|
+
|
14
|
+
mb->q0 = malloc(size);
|
15
|
+
mb->z = malloc(size);
|
16
|
+
mb->f = malloc(size);
|
17
|
+
mb->sqr_r = malloc(size);
|
18
|
+
|
19
|
+
mb->v = malloc(size);
|
20
|
+
mb->a = malloc(size);
|
21
|
+
mb->c = malloc(size);
|
22
|
+
|
23
|
+
int i;
|
24
|
+
for(i = 0; i < dim + 1; ++i) {
|
25
|
+
mb->v[i] = malloc(innerSize);
|
26
|
+
mb->a[i] = malloc(innerSize);
|
27
|
+
mb->c[i] = malloc(innerSize);
|
28
|
+
}
|
29
|
+
|
30
|
+
MiniballBuilder_reset(mb);
|
31
|
+
|
32
|
+
return mb;
|
33
|
+
}
|
34
|
+
|
35
|
+
void MiniballBuilder_destroy(MiniballBuilder* mb) {
|
36
|
+
int i;
|
37
|
+
for(i = 0; i < mb->d + 1; ++i) {
|
38
|
+
free(mb->v[i]);
|
39
|
+
free(mb->a[i]);
|
40
|
+
free(mb->c[i]);
|
41
|
+
}
|
42
|
+
|
43
|
+
free(mb->v);
|
44
|
+
free(mb->a);
|
45
|
+
free(mb->c);
|
46
|
+
|
47
|
+
free(mb->q0);
|
48
|
+
free(mb->z);
|
49
|
+
free(mb->f);
|
50
|
+
free(mb->sqr_r);
|
51
|
+
|
52
|
+
free(mb);
|
53
|
+
}
|
54
|
+
|
55
|
+
const double* MiniballBuilder_center(MiniballBuilder* mb) {
|
56
|
+
return mb->current_c;
|
57
|
+
}
|
58
|
+
|
59
|
+
double MiniballBuilder_squared_radius(MiniballBuilder* mb) {
|
60
|
+
return mb->current_sqr_r;
|
61
|
+
}
|
62
|
+
|
63
|
+
int MiniballBuilder_size(MiniballBuilder* mb) {
|
64
|
+
return mb->m;
|
65
|
+
}
|
66
|
+
|
67
|
+
int MiniballBuilder_support_size(MiniballBuilder* mb) {
|
68
|
+
return mb->s;
|
69
|
+
}
|
70
|
+
|
71
|
+
double MiniballBuilder_excess(MiniballBuilder* mb, const Point* p) {
|
72
|
+
double e = -mb->current_sqr_r;
|
73
|
+
int i;
|
74
|
+
for(i = 0; i < mb->d; ++i) {
|
75
|
+
e += sqr(p->coord[i] - mb->current_c[i]);
|
76
|
+
}
|
77
|
+
return e;
|
78
|
+
}
|
79
|
+
|
80
|
+
void MiniballBuilder_reset(MiniballBuilder* mb) {
|
81
|
+
mb->m = mb->s = 0;
|
82
|
+
int i;
|
83
|
+
for(i = 0; i < mb->d; ++i) {
|
84
|
+
mb->c[0][i] = 0;
|
85
|
+
}
|
86
|
+
mb->current_c = mb->c[0];
|
87
|
+
mb->current_sqr_r = -1;
|
88
|
+
}
|
89
|
+
|
90
|
+
//TODO: Document
|
91
|
+
int MiniballBuilder_push(MiniballBuilder* mb, Point* p) {
|
92
|
+
int supPoint = mb->m;
|
93
|
+
int dim = mb->d;
|
94
|
+
|
95
|
+
int i, j;
|
96
|
+
double eps = 1e-32;
|
97
|
+
if(supPoint == 0) {
|
98
|
+
for(i = 0; i < dim; ++i) {
|
99
|
+
double c = p->coord[i];
|
100
|
+
mb->q0[i] = c;
|
101
|
+
mb->c[0][i] = c;
|
102
|
+
}
|
103
|
+
mb->sqr_r[0] = 0;
|
104
|
+
} else {
|
105
|
+
for(i = 0; i < dim; ++i) {
|
106
|
+
//Set v_m to Q_m
|
107
|
+
mb->v[supPoint][i] = p->coord[i] - mb->q0[i];
|
108
|
+
}
|
109
|
+
|
110
|
+
//Compute the a_{supPoint,i}, i < supPoint
|
111
|
+
for(i = 1; i < supPoint; ++i) {
|
112
|
+
double* a = mb->a[supPoint];
|
113
|
+
a[i] = 0;
|
114
|
+
for(j = 0; j < dim; ++j) {
|
115
|
+
a[i] += mb->v[i][j] * mb->v[supPoint][j];
|
116
|
+
}
|
117
|
+
a[i] *= (2 / mb->z[i]);
|
118
|
+
}
|
119
|
+
|
120
|
+
//Update v_m to Q_m - \bar{Q}_m
|
121
|
+
for(i = 1; i < supPoint; ++i) {
|
122
|
+
for(j = 0; j < dim; ++j) {
|
123
|
+
mb->v[supPoint][j] -= mb->a[supPoint][i] * mb->v[i][j];
|
124
|
+
}
|
125
|
+
}
|
126
|
+
|
127
|
+
//compute z_m
|
128
|
+
mb->z[supPoint] = 0;
|
129
|
+
for(j = 0; j < dim; ++j) {
|
130
|
+
mb->z[supPoint] += sqr(mb->v[supPoint][j]);
|
131
|
+
}
|
132
|
+
mb->z[supPoint] *= 2;
|
133
|
+
|
134
|
+
//Reject push if z_m too small
|
135
|
+
if(mb->z[supPoint] < eps * mb->current_sqr_r) {
|
136
|
+
return 0;
|
137
|
+
}
|
138
|
+
|
139
|
+
//Update c, sqr_c
|
140
|
+
double e = -mb->sqr_r[supPoint - 1];
|
141
|
+
for(i = 0; i < dim; ++i) {
|
142
|
+
e += sqr(p->coord[i] - mb->c[supPoint - 1][i]);
|
143
|
+
}
|
144
|
+
double f = e / mb->z[supPoint];
|
145
|
+
mb->f[supPoint] = f;
|
146
|
+
|
147
|
+
for(i = 0; i < dim; ++i) {
|
148
|
+
mb->c[supPoint][i] = mb->c[supPoint - 1][i] + f * mb->v[supPoint][i];
|
149
|
+
}
|
150
|
+
mb->sqr_r[supPoint] = mb->sqr_r[supPoint - 1] + e * f / 2;
|
151
|
+
}
|
152
|
+
|
153
|
+
mb->current_c = mb->c[supPoint];
|
154
|
+
mb->current_sqr_r = mb->sqr_r[supPoint];
|
155
|
+
mb->s = ++mb->m;
|
156
|
+
return 1;
|
157
|
+
}
|
158
|
+
|
159
|
+
void MiniballBuilder_pop(MiniballBuilder* mb) {
|
160
|
+
mb->m--;
|
161
|
+
}
|
162
|
+
|
163
|
+
double MiniballBuilder_slack(MiniballBuilder* mb) {
|
164
|
+
double* l = malloc(sizeof(double) * (mb->d + 1));
|
165
|
+
double min_l = 0;
|
166
|
+
l[0] = 1;
|
167
|
+
|
168
|
+
int i, k;
|
169
|
+
for(i = mb->s - 1; i > 0; --i) {
|
170
|
+
l[i] = mb->f[i];
|
171
|
+
for(k = mb->s - 1; k > i; --k) {
|
172
|
+
l[i] -= mb->a[k][i] * l[k];
|
173
|
+
}
|
174
|
+
if(l[i] < min_l) {
|
175
|
+
min_l = l[i];
|
176
|
+
}
|
177
|
+
l[0] -= l[i];
|
178
|
+
}
|
179
|
+
|
180
|
+
if(l[0] < min_l) {
|
181
|
+
min_l = l[0];
|
182
|
+
}
|
183
|
+
|
184
|
+
free(l);
|
185
|
+
|
186
|
+
return ((min_l < 0) ? -min_l : 0);
|
187
|
+
}
|
188
|
+
|
@@ -0,0 +1,43 @@
|
|
1
|
+
#ifndef __MINIBALL_B_DYNAMIC_D__
|
2
|
+
#define __MINIBALL_B_DYNAMIC_D__
|
3
|
+
|
4
|
+
#include "point_dynamic_d.h"
|
5
|
+
|
6
|
+
//Smallest enclosing ball with a set of n <= d+1 points *on the boundry*
|
7
|
+
typedef struct MiniballBuilder MiniballBuilder;
|
8
|
+
|
9
|
+
struct MiniballBuilder {
|
10
|
+
int d; //Dimension
|
11
|
+
int m, s; //Size and number of support points
|
12
|
+
double* q0;
|
13
|
+
|
14
|
+
double* z;
|
15
|
+
double* f;
|
16
|
+
double** v;
|
17
|
+
double** a;
|
18
|
+
|
19
|
+
double** c;
|
20
|
+
double* sqr_r;
|
21
|
+
|
22
|
+
double* current_c; //Refers to some c[j]
|
23
|
+
double current_sqr_r;
|
24
|
+
};
|
25
|
+
|
26
|
+
MiniballBuilder* MiniballBuilder_create(int dim);
|
27
|
+
void MiniballBuilder_destroy(MiniballBuilder* mb);
|
28
|
+
|
29
|
+
const double* MiniballBuilder_center(MiniballBuilder* mb);
|
30
|
+
double MiniballBuilder_squared_radius(MiniballBuilder* mb);
|
31
|
+
|
32
|
+
int MiniballBuilder_size(MiniballBuilder* mb);
|
33
|
+
int MiniballBuilder_support_size(MiniballBuilder* mb);
|
34
|
+
double MiniballBuilder_excess(MiniballBuilder* mb, const Point* p);
|
35
|
+
|
36
|
+
void MiniballBuilder_reset(MiniballBuilder* mb);
|
37
|
+
|
38
|
+
int MiniballBuilder_push(MiniballBuilder* mb, Point* p);
|
39
|
+
void MiniballBuilder_pop(MiniballBuilder* mb);
|
40
|
+
|
41
|
+
double MiniballBuilder_slack(MiniballBuilder* mb);
|
42
|
+
|
43
|
+
#endif
|
Binary file
|
@@ -0,0 +1,207 @@
|
|
1
|
+
#include <stdlib.h>
|
2
|
+
#include "util.h"
|
3
|
+
#include "point_dynamic_d.h"
|
4
|
+
#include "miniball_builder_dynamic_d.h"
|
5
|
+
#include "miniball_dynamic_d.h"
|
6
|
+
|
7
|
+
Miniball* Miniball_create(int dim) {
|
8
|
+
Miniball* m = malloc(sizeof(Miniball));
|
9
|
+
|
10
|
+
m->d = dim;
|
11
|
+
m->B = MiniballBuilder_create(dim);
|
12
|
+
|
13
|
+
m->firstPoint = 0;
|
14
|
+
m->lastPoint = 0;
|
15
|
+
m->pointCount = 0;
|
16
|
+
|
17
|
+
return m;
|
18
|
+
}
|
19
|
+
|
20
|
+
void Miniball_destroy(Miniball* m) {
|
21
|
+
if(m->firstPoint != 0) {
|
22
|
+
Point* p = m->firstPoint;
|
23
|
+
while(p) {
|
24
|
+
Point* next = p->next;
|
25
|
+
Point_destroy(p);
|
26
|
+
p = next;
|
27
|
+
}
|
28
|
+
}
|
29
|
+
MiniballBuilder_destroy(m->B);
|
30
|
+
free(m);
|
31
|
+
}
|
32
|
+
|
33
|
+
void Miniball_check_in(Miniball* m, Point* p) {
|
34
|
+
if(m->firstPoint == 0) {
|
35
|
+
m->firstPoint = p;
|
36
|
+
m->lastPoint = p;
|
37
|
+
} else {
|
38
|
+
p->prev = m->lastPoint;
|
39
|
+
m->lastPoint->next = p;
|
40
|
+
m->lastPoint = p;
|
41
|
+
}
|
42
|
+
m->pointCount++;
|
43
|
+
}
|
44
|
+
|
45
|
+
void Miniball_build(Miniball* m) {
|
46
|
+
//The c++ version used std::list::end, which is _beyond_ the last element
|
47
|
+
//Tack a fake point on to the end of our list to emulate this, while avoiding
|
48
|
+
//any segfaults in post-run analysis that would be caused by using void.
|
49
|
+
Point* hack = Point_create(m->d);
|
50
|
+
Miniball_check_in(m, hack);
|
51
|
+
|
52
|
+
MiniballBuilder_reset(m->B);
|
53
|
+
m->support_end = m->firstPoint;
|
54
|
+
Miniball_pivot_mb(m, m->lastPoint);
|
55
|
+
}
|
56
|
+
|
57
|
+
void Miniball_move_to_front(Miniball* m, Point* p) {
|
58
|
+
if(m->support_end == p) {
|
59
|
+
m->support_end = p->next;
|
60
|
+
}
|
61
|
+
|
62
|
+
if(m->firstPoint == p) {
|
63
|
+
return;
|
64
|
+
}
|
65
|
+
|
66
|
+
if(p->prev) {
|
67
|
+
p->prev->next = p->next;
|
68
|
+
}
|
69
|
+
if(p->next) {
|
70
|
+
p->next->prev = p->prev;
|
71
|
+
}
|
72
|
+
m->firstPoint->prev = p;
|
73
|
+
p->next = m->firstPoint;
|
74
|
+
p->prev = 0;
|
75
|
+
m->firstPoint = p;
|
76
|
+
}
|
77
|
+
|
78
|
+
void Miniball_mtf_mb(Miniball* m, Point* i) {
|
79
|
+
m->support_end = m->firstPoint;
|
80
|
+
if(MiniballBuilder_size(m->B) == m->d + 1) {
|
81
|
+
return;
|
82
|
+
}
|
83
|
+
|
84
|
+
Point* k = m->firstPoint;
|
85
|
+
while(k != i) {
|
86
|
+
Point* next = k->next;
|
87
|
+
if(MiniballBuilder_excess(m->B, k) > 0) {
|
88
|
+
if(MiniballBuilder_push(m->B, k)) {
|
89
|
+
Miniball_mtf_mb(m, k);
|
90
|
+
MiniballBuilder_pop(m->B);
|
91
|
+
Miniball_move_to_front(m, k);
|
92
|
+
}
|
93
|
+
}
|
94
|
+
k = next;
|
95
|
+
}
|
96
|
+
}
|
97
|
+
|
98
|
+
void Miniball_pivot_mb(Miniball* m, Point* p) {
|
99
|
+
Point* t = m->firstPoint->next;
|
100
|
+
Miniball_mtf_mb(m, t);
|
101
|
+
|
102
|
+
double max_e, old_sqr_r = -1;
|
103
|
+
|
104
|
+
do {
|
105
|
+
Point* pivot;
|
106
|
+
max_e = Miniball_max_excess(m, t, p, &pivot);
|
107
|
+
if(max_e > 0) {
|
108
|
+
t = m->support_end;
|
109
|
+
if(t == pivot) {
|
110
|
+
t = t->next;
|
111
|
+
}
|
112
|
+
old_sqr_r = MiniballBuilder_squared_radius(m->B);
|
113
|
+
MiniballBuilder_push(m->B, pivot);
|
114
|
+
Miniball_mtf_mb(m, m->support_end);
|
115
|
+
MiniballBuilder_pop(m->B);
|
116
|
+
Miniball_move_to_front(m, pivot);
|
117
|
+
}
|
118
|
+
} while((max_e > 0) && (MiniballBuilder_squared_radius(m->B) > old_sqr_r));
|
119
|
+
}
|
120
|
+
|
121
|
+
double Miniball_max_excess(Miniball* m, Point* t, Point* i, Point** pivot) {
|
122
|
+
const double* c = MiniballBuilder_center(m->B);
|
123
|
+
const double sqr_r = MiniballBuilder_squared_radius(m->B);
|
124
|
+
double e, max_e = 0;
|
125
|
+
|
126
|
+
Point* k = t;
|
127
|
+
while(k != i) {
|
128
|
+
e = -sqr_r;
|
129
|
+
int j;
|
130
|
+
for(j = 0; j < m->d; ++j) {
|
131
|
+
e += sqr(k->coord[j] - c[j]);
|
132
|
+
}
|
133
|
+
if(e > max_e) {
|
134
|
+
max_e = e;
|
135
|
+
*pivot = k;
|
136
|
+
}
|
137
|
+
k = k->next;
|
138
|
+
}
|
139
|
+
|
140
|
+
return max_e;
|
141
|
+
}
|
142
|
+
|
143
|
+
Point* Miniball_center(Miniball* m) {
|
144
|
+
Point* p = Point_create(m->d);
|
145
|
+
Point_copy_dbl(p, MiniballBuilder_center(m->B));
|
146
|
+
return p;
|
147
|
+
}
|
148
|
+
|
149
|
+
double Miniball_squared_radius(Miniball* m) {
|
150
|
+
return MiniballBuilder_squared_radius(m->B);
|
151
|
+
}
|
152
|
+
|
153
|
+
int Miniball_nr_points(Miniball* m) {
|
154
|
+
return m->pointCount;
|
155
|
+
}
|
156
|
+
|
157
|
+
Point* Miniball_points_begin(Miniball* m) {
|
158
|
+
return m->firstPoint;
|
159
|
+
}
|
160
|
+
|
161
|
+
Point* Miniball_points_end(Miniball* m) {
|
162
|
+
return m->lastPoint;
|
163
|
+
}
|
164
|
+
|
165
|
+
int Miniball_nr_support_points(Miniball* m) {
|
166
|
+
return MiniballBuilder_support_size(m->B);
|
167
|
+
}
|
168
|
+
|
169
|
+
Point* Miniball_support_points_begin(Miniball* m) {
|
170
|
+
return m->firstPoint;
|
171
|
+
}
|
172
|
+
|
173
|
+
Point* Miniball_support_points_end(Miniball* m) {
|
174
|
+
return m->support_end;
|
175
|
+
}
|
176
|
+
|
177
|
+
double Miniball_accuracy(Miniball* m, double* slack) {
|
178
|
+
double e, max_e = 0;
|
179
|
+
int n_supp = 0;
|
180
|
+
Point* i = m->firstPoint;
|
181
|
+
while(i != m->support_end) {
|
182
|
+
e = MiniballBuilder_excess(m->B, i);
|
183
|
+
if(e < 0) {
|
184
|
+
e = -e;
|
185
|
+
}
|
186
|
+
if(e > max_e) {
|
187
|
+
max_e = e;
|
188
|
+
}
|
189
|
+
i = i->next;
|
190
|
+
}
|
191
|
+
|
192
|
+
while(i != m->lastPoint) {
|
193
|
+
e = MiniballBuilder_excess(m->B, i);
|
194
|
+
if(e > max_e) {
|
195
|
+
max_e = e;
|
196
|
+
}
|
197
|
+
i = i->next;
|
198
|
+
}
|
199
|
+
|
200
|
+
*slack = MiniballBuilder_slack(m->B);
|
201
|
+
return (max_e / Miniball_squared_radius(m));
|
202
|
+
}
|
203
|
+
|
204
|
+
int Miniball_is_valid(Miniball* m, double tolerance) {
|
205
|
+
double slack;
|
206
|
+
return ((Miniball_accuracy(m, &slack) < tolerance) && (slack == 0));
|
207
|
+
}
|
@@ -0,0 +1,43 @@
|
|
1
|
+
#ifndef __MINIBALL_DYNAMIC_D__
|
2
|
+
#define __MINIBALL_DYNAMIC_D__
|
3
|
+
|
4
|
+
#include "point_dynamic_d.h"
|
5
|
+
#include "miniball_builder_dynamic_d.h"
|
6
|
+
|
7
|
+
//Smallest enclosing ball of a set of n points in dimension d
|
8
|
+
typedef struct Miniball Miniball;
|
9
|
+
|
10
|
+
struct Miniball {
|
11
|
+
int d; //Dimension
|
12
|
+
MiniballBuilder* B; //The current ball
|
13
|
+
|
14
|
+
Point* firstPoint; //Start of internal point set
|
15
|
+
Point* lastPoint; //End of internal point set
|
16
|
+
int pointCount;
|
17
|
+
|
18
|
+
Point* support_end; //Past-the end iterator of support set
|
19
|
+
};
|
20
|
+
|
21
|
+
Miniball* Miniball_create(int dim);
|
22
|
+
void Miniball_destroy(Miniball* m);
|
23
|
+
|
24
|
+
void Miniball_check_in(Miniball* m, Point* p);
|
25
|
+
void Miniball_build(Miniball* m);
|
26
|
+
void Miniball_move_to_front(Miniball* m, Point* p);
|
27
|
+
void Miniball_mtf_mb(Miniball* m, Point* i);
|
28
|
+
void Miniball_pivot_mb(Miniball* m, Point* p);
|
29
|
+
|
30
|
+
double Miniball_max_excess(Miniball* m, Point* t, Point* i, Point** pivot);
|
31
|
+
|
32
|
+
Point* Miniball_center(Miniball* m);
|
33
|
+
double Miniball_squared_radius(Miniball* m);
|
34
|
+
int Miniball_nr_points(Miniball* m);
|
35
|
+
Point* Miniball_points_begin(Miniball* m);
|
36
|
+
Point* Miniball_points_end(Miniball* m);
|
37
|
+
int Miniball_nr_support_points(Miniball* m);
|
38
|
+
Point* Miniball_support_points_begin(Miniball* m);
|
39
|
+
Point* Miniball_support_points_end(Miniball* m);
|
40
|
+
double Miniball_accuracy(Miniball* m, double* slack);
|
41
|
+
int Miniball_is_valid(Miniball* m, double tolerance);
|
42
|
+
|
43
|
+
#endif
|
Binary file
|
@@ -0,0 +1,169 @@
|
|
1
|
+
//TODO: Can push some more c ruby into this (and dependencies), like ALLOC
|
2
|
+
|
3
|
+
#include "ruby.h"
|
4
|
+
#include "util.h"
|
5
|
+
#include "point_dynamic_d.h"
|
6
|
+
#include "miniball_builder_dynamic_d.h"
|
7
|
+
#include "miniball_dynamic_d.h"
|
8
|
+
|
9
|
+
//
|
10
|
+
// Calculation result structue and accessors
|
11
|
+
//
|
12
|
+
|
13
|
+
VALUE MiniballResult;
|
14
|
+
|
15
|
+
typedef struct Result Result;
|
16
|
+
|
17
|
+
struct Result {
|
18
|
+
VALUE center;
|
19
|
+
VALUE radius_squared;
|
20
|
+
VALUE support_points;
|
21
|
+
VALUE accuracy;
|
22
|
+
VALUE slack;
|
23
|
+
};
|
24
|
+
|
25
|
+
Result* ConstructResult(int dim, double* center, double radius_squared) {
|
26
|
+
Result* r = malloc(sizeof(Result));
|
27
|
+
|
28
|
+
r->center = rb_ary_new2(dim);
|
29
|
+
int i;
|
30
|
+
for(i = 0; i < dim; ++i) {
|
31
|
+
rb_ary_store(r->center, i, rb_float_new(center[i]));
|
32
|
+
}
|
33
|
+
|
34
|
+
r->radius_squared = rb_float_new(radius_squared);
|
35
|
+
|
36
|
+
r->support_points = Qnil;
|
37
|
+
r->accuracy = Qnil;
|
38
|
+
r->slack = Qnil;
|
39
|
+
|
40
|
+
return r;
|
41
|
+
}
|
42
|
+
|
43
|
+
void MarkResult(Result* r) {
|
44
|
+
rb_gc_mark(r->center);
|
45
|
+
rb_gc_mark(r->radius_squared);
|
46
|
+
rb_gc_mark(r->support_points);
|
47
|
+
rb_gc_mark(r->accuracy);
|
48
|
+
rb_gc_mark(r->slack);
|
49
|
+
}
|
50
|
+
|
51
|
+
void DestroyResult(Result* r) {
|
52
|
+
free(r);
|
53
|
+
}
|
54
|
+
|
55
|
+
VALUE center(VALUE self) {
|
56
|
+
Result* r;
|
57
|
+
Data_Get_Struct(self, Result, r);
|
58
|
+
return r->center;
|
59
|
+
}
|
60
|
+
|
61
|
+
VALUE radius_squared(VALUE self) {
|
62
|
+
Result* r;
|
63
|
+
Data_Get_Struct(self, Result, r);
|
64
|
+
return r->radius_squared;
|
65
|
+
}
|
66
|
+
|
67
|
+
VALUE support_points(VALUE self) {
|
68
|
+
Result* r;
|
69
|
+
Data_Get_Struct(self, Result, r);
|
70
|
+
return r->support_points;
|
71
|
+
}
|
72
|
+
|
73
|
+
VALUE accuracy(VALUE self) {
|
74
|
+
Result* r;
|
75
|
+
Data_Get_Struct(self, Result, r);
|
76
|
+
return r->accuracy;
|
77
|
+
}
|
78
|
+
|
79
|
+
VALUE slack(VALUE self) {
|
80
|
+
Result* r;
|
81
|
+
Data_Get_Struct(self, Result, r);
|
82
|
+
return r->slack;
|
83
|
+
}
|
84
|
+
|
85
|
+
|
86
|
+
|
87
|
+
//
|
88
|
+
// Calculation methods
|
89
|
+
//
|
90
|
+
|
91
|
+
VALUE MiniballC;
|
92
|
+
|
93
|
+
static VALUE calc(VALUE self, VALUE points, VALUE with_analytics) {
|
94
|
+
Check_Type(points, T_ARRAY);
|
95
|
+
if(RARRAY_LEN(points) == 0) {
|
96
|
+
rb_raise(rb_eArgError, "Must have at least one point");
|
97
|
+
}
|
98
|
+
|
99
|
+
Check_Type(rb_ary_entry(points, 0), T_ARRAY);
|
100
|
+
int dim = RARRAY_LEN(rb_ary_entry(points, 0));
|
101
|
+
|
102
|
+
Miniball* m = Miniball_create(dim);
|
103
|
+
|
104
|
+
int i, j;
|
105
|
+
int len = RARRAY_LEN(points);
|
106
|
+
for(i = 0; i < len; ++i) {
|
107
|
+
VALUE point = rb_ary_entry(points, i);
|
108
|
+
Check_Type(point, T_ARRAY);
|
109
|
+
if(RARRAY_LEN(point) != dim) {
|
110
|
+
rb_raise(rb_eArgError, "Point dimensions must be consistent");
|
111
|
+
}
|
112
|
+
|
113
|
+
Point* p = Point_create(dim);
|
114
|
+
for(j = 0; j < dim; ++j) {
|
115
|
+
p->coord[j] = NUM2DBL(rb_ary_entry(point, j));
|
116
|
+
}
|
117
|
+
Miniball_check_in(m, p);
|
118
|
+
}
|
119
|
+
|
120
|
+
Miniball_build(m);
|
121
|
+
|
122
|
+
Point* center = Miniball_center(m);
|
123
|
+
double radius_squared = Miniball_squared_radius(m);
|
124
|
+
//TODO: Support points
|
125
|
+
|
126
|
+
Result* r = ConstructResult(dim, center->coord, radius_squared);
|
127
|
+
|
128
|
+
int support_point_count = Miniball_nr_support_points(m);
|
129
|
+
r->support_points = rb_ary_new2(support_point_count);
|
130
|
+
|
131
|
+
Point* support_point = Miniball_support_points_begin(m);
|
132
|
+
for(i = 0; i < support_point_count; i++, support_point = support_point->next) {
|
133
|
+
VALUE point = rb_ary_new2(dim);
|
134
|
+
for(j = 0; j < dim; ++j) {
|
135
|
+
rb_ary_store(point, j, rb_float_new(support_point->coord[j]));
|
136
|
+
}
|
137
|
+
rb_ary_store(r->support_points, i, point);
|
138
|
+
}
|
139
|
+
|
140
|
+
if(with_analytics) {
|
141
|
+
double slack;
|
142
|
+
r->accuracy = rb_float_new(Miniball_accuracy(m, &slack));
|
143
|
+
r->slack = slack;
|
144
|
+
}
|
145
|
+
|
146
|
+
VALUE result = Data_Wrap_Struct(MiniballResult, MarkResult, DestroyResult, r);
|
147
|
+
|
148
|
+
Miniball_destroy(m);
|
149
|
+
return result;
|
150
|
+
}
|
151
|
+
|
152
|
+
|
153
|
+
|
154
|
+
//
|
155
|
+
// Ruby init
|
156
|
+
//
|
157
|
+
|
158
|
+
void Init_miniball_ruby() {
|
159
|
+
MiniballC = rb_define_module("MiniballC");
|
160
|
+
rb_define_method(MiniballC, "calc", calc, 2);
|
161
|
+
|
162
|
+
MiniballResult = rb_define_class("MiniballResult", rb_cObject);
|
163
|
+
rb_define_method(MiniballResult, "center", center, 0);
|
164
|
+
rb_define_method(MiniballResult, "radius_squared", radius_squared, 0);
|
165
|
+
rb_define_method(MiniballResult, "support_points", support_points, 0);
|
166
|
+
rb_define_method(MiniballResult, "accuracy", accuracy, 0);
|
167
|
+
rb_define_method(MiniballResult, "slack", slack, 0);
|
168
|
+
}
|
169
|
+
|
Binary file
|
Binary file
|
@@ -0,0 +1,47 @@
|
|
1
|
+
#include <stdlib.h>
|
2
|
+
#include "point_dynamic_d.h"
|
3
|
+
|
4
|
+
Point* Point_create(int dim) {
|
5
|
+
Point* p = malloc(sizeof(Point));
|
6
|
+
|
7
|
+
p->d = dim;
|
8
|
+
p->coord = malloc(sizeof(double) * dim);
|
9
|
+
|
10
|
+
p->prev = 0;
|
11
|
+
p->next = 0;
|
12
|
+
|
13
|
+
return p;
|
14
|
+
}
|
15
|
+
|
16
|
+
void Point_destroy(Point* p) {
|
17
|
+
free(p->coord);
|
18
|
+
free(p);
|
19
|
+
}
|
20
|
+
|
21
|
+
void Point_copy_dbl(Point* dest, const double* source) {
|
22
|
+
int sourceDim = sizeof(source) / sizeof(double);
|
23
|
+
//assert(dest->d == sourceDim);
|
24
|
+
int i;
|
25
|
+
for(i = 0; i < dest->d; ++i) {
|
26
|
+
dest->coord[i] = source[i];
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
void Point_copy(Point* dest, const Point* source) {
|
31
|
+
int i;
|
32
|
+
if(dest->d != source->d) {
|
33
|
+
free(dest->coord);
|
34
|
+
dest->coord = malloc(sizeof(double) * source->d);
|
35
|
+
dest->d = source->d;
|
36
|
+
}
|
37
|
+
|
38
|
+
Point_copy_dbl(dest, source->coord);
|
39
|
+
}
|
40
|
+
|
41
|
+
double* Point_begin(Point* p) {
|
42
|
+
return p->coord;
|
43
|
+
}
|
44
|
+
|
45
|
+
double* Point_end(Point* p) {
|
46
|
+
return p->coord + p->d;
|
47
|
+
}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#ifndef __POINT__
|
2
|
+
#define __POINT__
|
3
|
+
|
4
|
+
typedef struct Point Point;
|
5
|
+
|
6
|
+
struct Point {
|
7
|
+
int d;
|
8
|
+
double* coord;
|
9
|
+
|
10
|
+
//Points can be used as doubly-linked lists (must free manually)
|
11
|
+
Point* prev;
|
12
|
+
Point* next;
|
13
|
+
};
|
14
|
+
|
15
|
+
Point* Point_create(int dim);
|
16
|
+
void Point_destroy(Point* p);
|
17
|
+
|
18
|
+
void Point_copy_dbl(Point* dest, const double* source);
|
19
|
+
void Point_copy(Point* dest, const Point* source);
|
20
|
+
|
21
|
+
double* Point_begin(Point* p);
|
22
|
+
double* Point_end(Point* p);
|
23
|
+
|
24
|
+
#endif
|
Binary file
|
Binary file
|
data/lib/miniball.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.expand_path('../miniball_ruby/miniball', __FILE__)
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'miniball_ruby/miniball_ruby'
|
2
|
+
|
3
|
+
class Miniball
|
4
|
+
include MiniballC
|
5
|
+
|
6
|
+
def initialize(points)
|
7
|
+
@result = nil
|
8
|
+
@points = points
|
9
|
+
end
|
10
|
+
|
11
|
+
def calculate(with_analysis = false)
|
12
|
+
@result ||= calc(@points, with_analysis || false)
|
13
|
+
end
|
14
|
+
|
15
|
+
def center
|
16
|
+
calculate.center
|
17
|
+
end
|
18
|
+
|
19
|
+
def radius
|
20
|
+
@radius ||= radius_squared ** 0.5
|
21
|
+
end
|
22
|
+
|
23
|
+
def radius_squared
|
24
|
+
calculate.radius_squared
|
25
|
+
end
|
26
|
+
|
27
|
+
def support_points
|
28
|
+
calculate.support_points
|
29
|
+
end
|
30
|
+
|
31
|
+
#Must calculate with analytics on to get accuracy or slack
|
32
|
+
|
33
|
+
def accuracy
|
34
|
+
unless @accuracy
|
35
|
+
a = calculate.accuracy
|
36
|
+
@accuracy = a.nan? ? 0.0 : a
|
37
|
+
end
|
38
|
+
@accuracy
|
39
|
+
end
|
40
|
+
|
41
|
+
def slack
|
42
|
+
unless @slack
|
43
|
+
s = calculate.slack
|
44
|
+
@slack = (s == false ? 0.0 : slack)
|
45
|
+
end
|
46
|
+
@slack
|
47
|
+
end
|
48
|
+
end
|
metadata
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: miniball_ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 2
|
10
|
+
version: 0.0.2
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Mike Marion
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-10-27 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: yajl-ruby
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 3
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
version: "0"
|
32
|
+
type: :development
|
33
|
+
version_requirements: *id001
|
34
|
+
description: |-
|
35
|
+
This gem finds minimum bounding spheres of a set of points.
|
36
|
+
It is a port of Bernd Gaertner's C++ miniball library.
|
37
|
+
email: mike.marion@gmail.com
|
38
|
+
executables: []
|
39
|
+
|
40
|
+
extensions:
|
41
|
+
- ext/miniball_ruby/extconf.rb
|
42
|
+
extra_rdoc_files: []
|
43
|
+
|
44
|
+
files:
|
45
|
+
- lib/miniball_ruby/miniball.rb
|
46
|
+
- lib/miniball_ruby/version.rb
|
47
|
+
- lib/miniball.rb
|
48
|
+
- ext/miniball_ruby/miniball_builder_dynamic_d.h
|
49
|
+
- ext/miniball_ruby/point_dynamic_d.o
|
50
|
+
- ext/miniball_ruby/util.c
|
51
|
+
- ext/miniball_ruby/miniball_builder_dynamic_d.c
|
52
|
+
- ext/miniball_ruby/extconf.rb
|
53
|
+
- ext/miniball_ruby/miniball_dynamic_d.o
|
54
|
+
- ext/miniball_ruby/miniball_ruby.o
|
55
|
+
- ext/miniball_ruby/point_dynamic_d.c
|
56
|
+
- ext/miniball_ruby/util.h
|
57
|
+
- ext/miniball_ruby/miniball_ruby.so
|
58
|
+
- ext/miniball_ruby/miniball_dynamic_d.c
|
59
|
+
- ext/miniball_ruby/miniball_builder_dynamic_d.o
|
60
|
+
- ext/miniball_ruby/miniball_ruby.c
|
61
|
+
- ext/miniball_ruby/Makefile
|
62
|
+
- ext/miniball_ruby/util.o
|
63
|
+
- ext/miniball_ruby/point_dynamic_d.h
|
64
|
+
- ext/miniball_ruby/miniball_dynamic_d.h
|
65
|
+
homepage: https://github.com/marionm/miniball_ruby
|
66
|
+
licenses: []
|
67
|
+
|
68
|
+
post_install_message:
|
69
|
+
rdoc_options: []
|
70
|
+
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
hash: 3
|
79
|
+
segments:
|
80
|
+
- 0
|
81
|
+
version: "0"
|
82
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
hash: 3
|
88
|
+
segments:
|
89
|
+
- 0
|
90
|
+
version: "0"
|
91
|
+
requirements: []
|
92
|
+
|
93
|
+
rubyforge_project:
|
94
|
+
rubygems_version: 1.8.11
|
95
|
+
signing_key:
|
96
|
+
specification_version: 3
|
97
|
+
summary: Ruby/C implementation of miniball
|
98
|
+
test_files: []
|
99
|
+
|