tioga 1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. data/Tioga_README +372 -0
  2. data/lgpl.txt +504 -0
  3. data/split/Dtable/defs.h +33 -0
  4. data/split/Dtable/dtable.c +1928 -0
  5. data/split/Dtable/dtable_intern.h +144 -0
  6. data/split/Dtable/dvector.h +61 -0
  7. data/split/Dtable/extconf.rb +4 -0
  8. data/split/Dtable/include/dtable.h +35 -0
  9. data/split/Dtable/lib/Dtable_extras.rb +90 -0
  10. data/split/Dtable/namespace.h +47 -0
  11. data/split/Dtable/safe_double.h +104 -0
  12. data/split/Dtable/symbols.c +92 -0
  13. data/split/Dtable/symbols.h +52 -0
  14. data/split/Dvector/defs.h +33 -0
  15. data/split/Dvector/dvector.c +5486 -0
  16. data/split/Dvector/dvector_intern.h +142 -0
  17. data/split/Dvector/extconf.rb +4 -0
  18. data/split/Dvector/include/dvector.h +61 -0
  19. data/split/Dvector/lib/Dvector_extras.rb +328 -0
  20. data/split/Dvector/lib/Numeric_extras.rb +134 -0
  21. data/split/Dvector/namespace.h +47 -0
  22. data/split/Dvector/safe_double.h +104 -0
  23. data/split/Dvector/symbols.c +92 -0
  24. data/split/Dvector/symbols.h +52 -0
  25. data/split/Flate/defs.h +33 -0
  26. data/split/Flate/extconf.rb +19 -0
  27. data/split/Flate/flate.c +156 -0
  28. data/split/Flate/flate_intern.h +97 -0
  29. data/split/Flate/include/flate.h +98 -0
  30. data/split/Flate/namespace.h +47 -0
  31. data/split/Flate/safe_double.h +104 -0
  32. data/split/Flate/symbols.c +92 -0
  33. data/split/Flate/symbols.h +52 -0
  34. data/split/Function/defs.h +33 -0
  35. data/split/Function/dvector.h +61 -0
  36. data/split/Function/extconf.rb +4 -0
  37. data/split/Function/function.c +988 -0
  38. data/split/Function/joint_qsort.c +258 -0
  39. data/split/Function/lib/Function_extras.rb +44 -0
  40. data/split/Function/namespace.h +47 -0
  41. data/split/Function/safe_double.h +104 -0
  42. data/split/Function/symbols.c +92 -0
  43. data/split/Function/symbols.h +52 -0
  44. data/split/Tioga/axes.c +774 -0
  45. data/split/Tioga/defs.h +33 -0
  46. data/split/Tioga/dtable.h +35 -0
  47. data/split/Tioga/dvector.h +61 -0
  48. data/split/Tioga/extconf.rb +4 -0
  49. data/split/Tioga/figures.c +672 -0
  50. data/split/Tioga/figures.h +855 -0
  51. data/split/Tioga/flate.h +98 -0
  52. data/split/Tioga/init.c +524 -0
  53. data/split/Tioga/lib/Arcs_and_Circles.rb +64 -0
  54. data/split/Tioga/lib/ColorConstants.rb +274 -0
  55. data/split/Tioga/lib/Colorbars.rb +10 -0
  56. data/split/Tioga/lib/Colormaps.rb +105 -0
  57. data/split/Tioga/lib/Coordinate_Conversions.rb +194 -0
  58. data/split/Tioga/lib/Creating_Paths.rb +94 -0
  59. data/split/Tioga/lib/Doc.rb +91 -0
  60. data/split/Tioga/lib/Executive.rb +515 -0
  61. data/split/Tioga/lib/FigMkr.rb +2224 -0
  62. data/split/Tioga/lib/FigureConstants.rb +125 -0
  63. data/split/Tioga/lib/Figures_and_Plots.rb +268 -0
  64. data/split/Tioga/lib/Images.rb +278 -0
  65. data/split/Tioga/lib/Legends.rb +190 -0
  66. data/split/Tioga/lib/MarkerConstants.rb +122 -0
  67. data/split/Tioga/lib/Markers.rb +129 -0
  68. data/split/Tioga/lib/Page_Frame_Bounds.rb +567 -0
  69. data/split/Tioga/lib/Rectangles.rb +94 -0
  70. data/split/Tioga/lib/Shading.rb +100 -0
  71. data/split/Tioga/lib/Special_Paths.rb +307 -0
  72. data/split/Tioga/lib/Strokes.rb +129 -0
  73. data/split/Tioga/lib/TeX_Text.rb +454 -0
  74. data/split/Tioga/lib/TexPreamble.rb +358 -0
  75. data/split/Tioga/lib/Titles_and_Labels.rb +306 -0
  76. data/split/Tioga/lib/Transparency.rb +89 -0
  77. data/split/Tioga/lib/Using_Paths.rb +164 -0
  78. data/split/Tioga/lib/Utils.rb +74 -0
  79. data/split/Tioga/lib/X_and_Y_Axes.rb +749 -0
  80. data/split/Tioga/lib/irb_tioga.rb +122 -0
  81. data/split/Tioga/lib/tioga.rb +1 -0
  82. data/split/Tioga/lib/tioga_ui.rb +5 -0
  83. data/split/Tioga/lib/tioga_ui_cmds.rb +793 -0
  84. data/split/Tioga/makers.c +989 -0
  85. data/split/Tioga/mk_tioga_sty.rb +53 -0
  86. data/split/Tioga/namespace.h +47 -0
  87. data/split/Tioga/pdf_font_dicts.c +18253 -0
  88. data/split/Tioga/pdfcolor.c +486 -0
  89. data/split/Tioga/pdfcoords.c +505 -0
  90. data/split/Tioga/pdffile.c +342 -0
  91. data/split/Tioga/pdfimage.c +536 -0
  92. data/split/Tioga/pdfpath.c +914 -0
  93. data/split/Tioga/pdfs.h +229 -0
  94. data/split/Tioga/pdftext.c +443 -0
  95. data/split/Tioga/safe_double.h +104 -0
  96. data/split/Tioga/symbols.c +92 -0
  97. data/split/Tioga/symbols.h +52 -0
  98. data/split/Tioga/texout.c +380 -0
  99. data/split/defs.h +33 -0
  100. data/split/extconf.rb +107 -0
  101. data/split/mkmf2.rb +1612 -0
  102. data/split/namespace.h +47 -0
  103. data/split/safe_double.h +104 -0
  104. data/split/scripts/tioga +4 -0
  105. data/split/symbols.c +92 -0
  106. data/split/symbols.h +52 -0
  107. data/tests/dtable_test.data +6 -0
  108. data/tests/dvector_read_test.data +1 -0
  109. data/tests/dvector_test.data +101 -0
  110. data/tests/tc_Dtable.rb +221 -0
  111. data/tests/tc_Dvector.rb +791 -0
  112. data/tests/tc_FMkr.rb +162 -0
  113. data/tests/tc_Flate.rb +45 -0
  114. data/tests/tc_Function.rb +111 -0
  115. data/tests/ts_Tioga.rb +38 -0
  116. metadata +163 -0
@@ -0,0 +1,92 @@
1
+ /*
2
+ Copyright (C) 2006 Vincent Fourmond
3
+
4
+ Symbols is free software; you can redistribute it and/or modify
5
+ it under the terms of the GNU General Library Public License as published
6
+ by the Free Software Foundation; either version 2 of the License, or
7
+ (at your option) any later version.
8
+
9
+ Symbols is distributed in the hope that it will be useful,
10
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ GNU Library General Public License for more details.
13
+
14
+ You should have received a copy of the GNU Library General Public License
15
+ along with Dvector; if not, write to the Free Software
16
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
+ */
18
+
19
+
20
+ /* Simple code for sharing C symbols across different Ruby libraries */
21
+
22
+ #include <ruby.h>
23
+ #include <intern.h>
24
+
25
+ #include <namespace.h>
26
+
27
+
28
+ /* MV stands for Module Variable */
29
+ #define MV_SYMBOLS "@_exported_C_symbols"
30
+ /* modified to use instance variables instead of global class variables:
31
+ this way, children don't overwrite the parent's export table
32
+ */
33
+
34
+
35
+ /* makes sure that the hash is registered for the given
36
+ module and returns it */
37
+ static VALUE get_symbol_hash(VALUE module)
38
+ {
39
+ VALUE hash;
40
+ ID mv_id = rb_intern(MV_SYMBOLS);
41
+ if(RTEST(rb_ivar_defined(module, mv_id)))
42
+ {
43
+ hash = rb_ivar_get(module, mv_id);
44
+ Check_Type(hash, T_HASH);
45
+ return hash;
46
+ }
47
+ else
48
+ {
49
+ /* module variable uninitialized, we need to make sure it's here */
50
+ hash = rb_hash_new();
51
+ rb_ivar_set(module, mv_id, hash);
52
+ return hash;
53
+ }
54
+ }
55
+
56
+ /* registers a symbol in the given module. This one is the internal
57
+ function */
58
+ PRIVATE void rb_export_symbol(VALUE module, const char * symbol_name,
59
+ void * symbol)
60
+ {
61
+ VALUE hash = get_symbol_hash(module);
62
+ rb_hash_aset(hash, rb_str_new2(symbol_name),LONG2NUM((long) symbol));
63
+ }
64
+
65
+ PRIVATE void * rb_import_symbol_no_raise(VALUE module,
66
+ const char * symbol_name)
67
+ {
68
+ VALUE hash = rb_iv_get(module, MV_SYMBOLS);
69
+ if(TYPE(hash) != T_HASH)
70
+ return NULL; /* doesn't fail, but the importing module
71
+ should definitely check the return value. Beware
72
+ of segfaults ! */
73
+ VALUE symbol = rb_hash_aref(hash, rb_str_new2(symbol_name));
74
+
75
+ if(TYPE(symbol) == T_FIXNUM || TYPE(symbol) == T_BIGNUM)
76
+ return (void *) NUM2LONG(symbol);
77
+ return NULL;
78
+ }
79
+
80
+ /* same as before, but raises something is the return value is NULL,
81
+ which is probably best as a default behavior*/
82
+ PRIVATE void * rb_import_symbol(VALUE module, const char * symbol_name)
83
+ {
84
+ void * symbol = rb_import_symbol_no_raise(module, symbol_name);
85
+ if(symbol)
86
+ return symbol;
87
+ /* we get the name of the module: */
88
+ VALUE module_name = rb_funcall(module, rb_intern("to_s"), 0);
89
+ rb_raise(rb_eRuntimeError, "The symbol %s was not found in "
90
+ "module %s", symbol_name,
91
+ rb_string_value_cstr(&module_name));
92
+ }
@@ -0,0 +1,52 @@
1
+ #ifndef _SYMBOLS_H
2
+ #define _SYMBOLS_H
3
+
4
+ #include <ruby.h>
5
+ /* A small layer for exporting and importing symbols from
6
+ a compiled module */
7
+
8
+ #include <namespace.h>
9
+
10
+
11
+ PRIVATE void rb_export_symbol(VALUE module, const char * symbol_name,
12
+ void * symbol);
13
+ PRIVATE void * rb_import_symbol(VALUE module, const char * symbol_name);
14
+ PRIVATE void * rb_import_symbol_no_raise(VALUE module,
15
+ const char * symbol_name);
16
+
17
+ /* a shortcut for exporting something with the same name */
18
+ #define RB_EXPORT_SYMBOL(module, name) \
19
+ rb_export_symbol(module, #name, name)
20
+
21
+ /* A shortcut for getting a symbol */
22
+
23
+ #define DECLARE_SYMBOL(ret_type,name,args) \
24
+ typedef ret_type (*rb_export_##name##_type) args;\
25
+ PRIVATE rb_export_##name##_type name
26
+
27
+ #define IMPLEMENT_SYMBOL(name)\
28
+ PRIVATE rb_export_##name##_type name = 0;
29
+
30
+ #define RB_IMPORT_SYMBOL(module, name) \
31
+ name = (rb_export_##name##_type) rb_import_symbol(module, #name)
32
+
33
+
34
+ /* These three macro enable a simple use of function declaration: this way,
35
+ you can type in a header file that is used by the importer:
36
+
37
+ DECLARE_SYMBOL(int, biniou, (int, int));
38
+
39
+ in one file of the importer code, you write
40
+
41
+ IMPLEMENT_SYMBOL(biniou);
42
+
43
+ and in the Init_... function, you write:
44
+
45
+ RB_IMPORT_SYMBOL(module, biniou);
46
+
47
+ and you can just use biniou as if it was a function declared as
48
+ usual in the header file
49
+ */
50
+ #endif
51
+
52
+
@@ -0,0 +1,33 @@
1
+ /**********************************************************************
2
+
3
+ defs.h:
4
+ some definitions that are used everywhere and that depend on some
5
+ HAVE_* macros.
6
+
7
+ Copyright (C) 2006 Vincent Fourmond
8
+
9
+ This program is free software; you can redistribute it and/or modify
10
+ it under the terms of the GNU General Library Public License as published
11
+ by the Free Software Foundation; either version 2 of the License, or
12
+ (at your option) any later version.
13
+
14
+ This program is distributed in the hope that it will be useful,
15
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ GNU Library General Public License for more details.
18
+
19
+ You should have received a copy of the GNU Library General Public License
20
+ along with this program; if not, write to the Free Software
21
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
+
23
+ **********************************************************************/
24
+
25
+
26
+ #ifdef HAVE_ISNAN
27
+ /* we use isnan and isinf, which are much faster than the workaround */
28
+ #define is_okay_number(x) (! isnan(x) && ! isinf(x))
29
+ #else
30
+ #define is_okay_number(x) ((x) - (x) == 0.0)
31
+ #define isnan(x) ((x) != (x))
32
+ /* yes, as funny as it may look NaN != NaN, and that's the only one */
33
+ #endif
@@ -0,0 +1,61 @@
1
+ /* Dvector.h */
2
+ /*
3
+ Copyright (C) 2005 Bill Paxton
4
+
5
+ Dvector is free software; you can redistribute it and/or modify
6
+ it under the terms of the GNU General Library Public License as published
7
+ by the Free Software Foundation; either version 2 of the License, or
8
+ (at your option) any later version.
9
+
10
+ Dvector is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU Library General Public License for more details.
14
+
15
+ You should have received a copy of the GNU Library General Public License
16
+ along with Dvector; if not, write to the Free Software
17
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
+ */
19
+
20
+ #ifndef __Dvector_H__
21
+ #define __Dvector_H__
22
+
23
+ /* this file has been heavily modified by Vincent Fourmond to take care
24
+ of the 'RCR330' scheme of exporting symbols
25
+ */
26
+
27
+ #include <symbols.h>
28
+ #include <stdbool.h>
29
+
30
+ /*======================================================================*/
31
+
32
+ /* functions for handling Dvectors: */
33
+
34
+ DECLARE_SYMBOL(double *, Dvector_Data_for_Read, (VALUE dvector, long *len_ptr));
35
+ /* returns pointer to the dvector's data (which may be shared) */
36
+ DECLARE_SYMBOL(double *, Dvector_Data_Copy, (VALUE dvector, long *len_ptr));
37
+ /* like Dvector_Data_for_Read, but returns pointer to a copy of the data */
38
+ DECLARE_SYMBOL(double *, Dvector_Data_for_Write,
39
+ (VALUE dvector, long *len_ptr));
40
+ DECLARE_SYMBOL(double *, Dvector_Data_Resize, (VALUE dvector, long new_len));
41
+ DECLARE_SYMBOL(double *, Dvector_Data_Replace,
42
+ (VALUE dvector, long len, double *data));
43
+ /* copies the data into the dvector */
44
+ DECLARE_SYMBOL(VALUE, Dvector_Create, (void));
45
+ DECLARE_SYMBOL(void, Dvector_Store_Double, (VALUE ary, long idx, double val));
46
+ /* pushes one element onto the vector */
47
+ DECLARE_SYMBOL(void, Dvector_Push_Double, (VALUE ary, double val));
48
+
49
+ /* functions for interpolation */
50
+ DECLARE_SYMBOL(double, c_dvector_spline_interpolate,
51
+ (double x, int n_pts_data, double *Xs, double *Ys,
52
+ double *Bs, double *Cs, double *Ds));
53
+ DECLARE_SYMBOL(double, c_dvector_linear_interpolate,
54
+ (int num_pts, double *xs, double *ys, double x));
55
+ DECLARE_SYMBOL(void, c_dvector_create_spline_interpolant,
56
+ (int n_pts_data, double *Xs, double *Ys,
57
+ bool start_clamped, double start_slope,
58
+ bool end_clamped, double end_slope,
59
+ double *As, double *Bs, double *Cs));
60
+ #endif /* __Dvector_H__ */
61
+
@@ -0,0 +1,4 @@
1
+ # Dtable installation file
2
+ require 'mkmf'
3
+
4
+ create_makefile 'Dobjects/Function'
@@ -0,0 +1,988 @@
1
+ /**********************************************************************
2
+
3
+ Function.c
4
+
5
+ An object embedding two Dvectors for the ease of manipulation as
6
+ a function.
7
+
8
+ Copyright (C) 2006 Vincent Fourmond
9
+
10
+ This program is free software; you can redistribute it and/or modify
11
+ it under the terms of the GNU General Library Public License as published
12
+ by the Free Software Foundation; either version 2 of the License, or
13
+ (at your option) any later version.
14
+
15
+ This program is distributed in the hope that it will be useful,
16
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
17
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
+ GNU Library General Public License for more details.
19
+
20
+ You should have received a copy of the GNU Library General Public License
21
+ along with this program; if not, write to the Free Software
22
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23
+
24
+ **********************************************************************/
25
+
26
+ #include <namespace.h>
27
+ #include <ruby.h>
28
+
29
+ #include "dvector.h"
30
+
31
+ #include <math.h>
32
+ /* compiler-dependent definitions, such as is_okay_number */
33
+ #include <defs.h>
34
+
35
+ /* the class we're defining */
36
+ static VALUE cFunction;
37
+ static VALUE cDvector;
38
+
39
+ /* ID used by different functions */
40
+ static ID idSize;
41
+ static ID idSetDirty;
42
+ static ID idDirty;
43
+ static ID idSort;
44
+ static ID idNew;
45
+
46
+ /* a few macros to work with Dvectors */
47
+ #define IS_A_DVECTOR(x) RTEST(rb_obj_is_kind_of(x, cDvector))
48
+
49
+ /* returns the size of a Dvector object */
50
+ #define DVECTOR_SIZE(x) (NUM2LONG(rb_funcall(x, idSize,0)))
51
+
52
+ #define DVECTOR_IS_DIRTY(x) (RTEST(rb_funcall(x, idDirty,0)))
53
+ #define DVECTOR_CLEAR(x) (rb_funcall(x, idSetDirty,1, Qfalse))
54
+ #define NUMERIC(x) (rb_type(x) == T_FIXNUM || \
55
+ rb_type(x) == T_BIGNUM)
56
+
57
+ #define X_VAL "@x_val"
58
+ #define Y_VAL "@y_val"
59
+ #define SPLINE_CACHE "@spline_cache"
60
+
61
+
62
+
63
+ /* basic functions for accessing the objects */
64
+
65
+
66
+ inline
67
+ /*
68
+ The X vector.
69
+ */
70
+ static VALUE get_x_vector(VALUE self)
71
+ {
72
+ return rb_iv_get(self, X_VAL);
73
+ }
74
+
75
+ inline
76
+ static void set_x_vector(VALUE self, VALUE vector)
77
+ {
78
+ rb_iv_set(self, X_VAL, vector);
79
+ }
80
+
81
+ inline
82
+ /*
83
+ The Y vector.
84
+ */
85
+ static VALUE get_y_vector(VALUE self)
86
+ {
87
+ return rb_iv_get(self, Y_VAL);
88
+ }
89
+
90
+ inline
91
+ static void set_y_vector(VALUE self, VALUE vector)
92
+ {
93
+ rb_iv_set(self, Y_VAL, vector);
94
+ }
95
+
96
+
97
+ inline static VALUE get_spline_vector(VALUE self)
98
+ {
99
+ return rb_iv_get(self, SPLINE_CACHE);
100
+ }
101
+
102
+ inline static void set_spline_vector(VALUE self, VALUE vector)
103
+ {
104
+ rb_iv_set(self, SPLINE_CACHE, vector);
105
+ }
106
+
107
+
108
+ /*
109
+ Checks that self is a Function, that it has X and Y Dvectors and that
110
+ they both have the same size. In that case, the size is returned.
111
+ */
112
+ static long function_sanity_check(VALUE self)
113
+ {
114
+ if(RTEST(rb_obj_is_kind_of(self, cFunction)))
115
+ {
116
+ VALUE x = get_x_vector(self);
117
+ VALUE y = get_y_vector(self);
118
+ if(IS_A_DVECTOR(x)
119
+ && IS_A_DVECTOR(y))
120
+ {
121
+ long size = DVECTOR_SIZE(x);
122
+ if( size== DVECTOR_SIZE(y))
123
+ return size;
124
+ else
125
+ {
126
+ rb_raise(rb_eRuntimeError, "X and Y vectors must have the"
127
+ " same size");
128
+ return -1;
129
+ }
130
+ }
131
+ else
132
+ {
133
+ rb_raise(rb_eRuntimeError, "X and Y must be vectors");
134
+ return -1;
135
+ }
136
+ }
137
+ else
138
+ {
139
+ rb_raise(rb_eRuntimeError, "self is no Function");
140
+ return -1;
141
+ }
142
+ }
143
+
144
+
145
+ /*
146
+ call-seq:
147
+ Function.new(x,y)
148
+
149
+ Creates a Function object with given +x+ and +y+ values.
150
+ */
151
+ static VALUE function_initialize(VALUE self, VALUE x, VALUE y)
152
+ {
153
+ if(IS_A_DVECTOR(x) && IS_A_DVECTOR(y))
154
+ {
155
+ if(DVECTOR_SIZE(x) == DVECTOR_SIZE(y)) {
156
+ set_x_vector(self, x);
157
+ set_y_vector(self, y);
158
+ /* fine, this could have been written in pure Ruby...*/
159
+ }
160
+ else
161
+ rb_raise(rb_eArgError,"both vectors must have the same size");
162
+ }
163
+ else
164
+ rb_raise(rb_eArgError,"both arguments must be Dvector");
165
+ return self;
166
+ }
167
+
168
+ static VALUE Function_Create(VALUE x, VALUE y)
169
+ {
170
+ return rb_funcall(cFunction, idNew, 2, x, y);
171
+ }
172
+
173
+ static int dvector_is_sorted(VALUE dvector)
174
+ {
175
+ long size;
176
+ const double * x_data;
177
+ double prev;
178
+ if(! IS_A_DVECTOR(dvector))
179
+ rb_raise(rb_eArgError, "should take a Dvector as argument");
180
+ else
181
+ {
182
+ x_data = Dvector_Data_for_Read(dvector, &size);
183
+ prev = x_data[0];
184
+ while((--size) && prev <= *(++x_data))
185
+ prev = *x_data;
186
+ return (size == 0);
187
+ }
188
+ return 0;
189
+ }
190
+
191
+ /*
192
+ Checks if the X values of the Function are sorted.
193
+ */
194
+ static VALUE function_is_sorted(VALUE self)
195
+ {
196
+ if(dvector_is_sorted(get_x_vector(self)))
197
+ return Qtrue;
198
+ else
199
+ return Qfalse;
200
+ }
201
+
202
+ static VALUE function_sort(VALUE self);
203
+
204
+ /* small macros to make the code a little more clear */
205
+ #define FIXED_BOUNDARY(n, slope) (3.0/(x_vals[n+1] - x_vals[n])) *\
206
+ ((y_vals[n+1] - y_vals[n])/(x_vals[n+1] - x_vals[n]) - slope)
207
+
208
+
209
+ /* This code is greatly inspired by what can be found in the book
210
+ Numerical Recipes in C. It fills the y2_vals values with computed
211
+ second derivatives. left_der and right_der are boundary conditions.
212
+ If not finite, use natural spline.
213
+ */
214
+ static void function_fill_second_derivatives(long nb_points,
215
+ const double *x_vals,
216
+ const double *y_vals,
217
+ double * y2_vals,
218
+ double left_slope,
219
+ double right_slope)
220
+ {
221
+ if(nb_points < 1)
222
+ return;
223
+ double *tmp = (double *)ALLOC_N(double, nb_points);
224
+ long i;
225
+ double piv;
226
+ double ratio;
227
+
228
+ if(is_okay_number(left_slope)) /* slope is defined */
229
+ {
230
+ y2_vals[0] = -0.5;
231
+ tmp[0] = FIXED_BOUNDARY(0,left_slope);
232
+ }
233
+ else
234
+ y2_vals[0] = tmp[0] = 0; /* natural spline */
235
+
236
+ /* forward decomposition */
237
+ for(i = 1; i < nb_points - 1; i++)
238
+ {
239
+ ratio = (x_vals[i] - x_vals[i-1])/(x_vals[i+1] - x_vals[i-1]);
240
+ piv = 1/(ratio * y2_vals[i-1] + 2.0);
241
+ y2_vals[i] = (ratio - 1.0) * piv;
242
+ tmp[i] = (6.0 *
243
+ ((y_vals[i+1] - y_vals[i] )/
244
+ (x_vals[i+1] - x_vals[i] ) -
245
+ (y_vals[i] - y_vals[i-1])/
246
+ (x_vals[i] - x_vals[i-1])
247
+ )/
248
+ (x_vals[i+1] - x_vals[i-1])
249
+ - ratio * tmp[i-1]) * piv;
250
+ }
251
+ /* then, the right boundary condition */
252
+ if(is_okay_number(right_slope)) /* slope is defined */
253
+ {
254
+ y2_vals[nb_points - 1] = 0.5;
255
+ tmp[nb_points - 1] = - FIXED_BOUNDARY(nb_points - 2,right_slope);
256
+ }
257
+ else
258
+ y2_vals[nb_points - 1] = tmp[nb_points - 1] = 0; /* natural spline */
259
+
260
+ /* then, backward substitution */
261
+ y2_vals[nb_points - 1] = (tmp[nb_points - 1] -
262
+ y2_vals[nb_points - 1] * tmp[nb_points - 2])/
263
+ (y2_vals[nb_points - 1] * y2_vals[nb_points - 2] + 1.0);
264
+ for(i = nb_points - 2; i >= 0; i--)
265
+ y2_vals[i] = y2_vals[i]*y2_vals[i+1] + tmp[i];
266
+ /* done, we free the allocated buffer */
267
+ xfree(tmp);
268
+ }
269
+
270
+ /*
271
+ Computes spline data and caches it inside the object. Both X and Y vectors
272
+ are cleared (see Dvector#clear) to make sure the cache is kept up-to-date.
273
+ If the function is not sorted, sorts it.
274
+ */
275
+ static VALUE function_compute_spline_data(VALUE self)
276
+ {
277
+ VALUE x_vec = get_x_vector(self);
278
+ VALUE y_vec = get_y_vector(self);
279
+ VALUE cache = get_spline_vector(self);
280
+ long size = DVECTOR_SIZE(x_vec);
281
+
282
+ if(DVECTOR_SIZE(y_vec) != size)
283
+ rb_raise(rb_eRuntimeError,
284
+ "x and y should have the same size !");
285
+ if(! IS_A_DVECTOR(cache)) /* create it -- and silently ignores
286
+ its previous values */
287
+ cache = rb_funcall(cDvector, idNew,
288
+ 1, LONG2NUM(size));
289
+ if(DVECTOR_SIZE(cache) != size) /* switch to the required size for cache */
290
+ Dvector_Data_Resize(cache, size);
291
+
292
+ /* we make sure that the X values are sorted */
293
+ if(! RTEST(function_is_sorted(self)))
294
+ function_sort(self);
295
+
296
+ double * x, *y, *spline;
297
+ x = Dvector_Data_for_Read(x_vec, NULL);
298
+ y = Dvector_Data_for_Read(y_vec, NULL);
299
+ spline = Dvector_Data_for_Write(cache, NULL);
300
+
301
+ function_fill_second_derivatives(size, x, y, spline,1.0/0.0, 1.0/0.0);
302
+ set_spline_vector(self, cache);
303
+
304
+ /* now, we clear both X and Y */
305
+ DVECTOR_CLEAR(x_vec);
306
+ DVECTOR_CLEAR(y_vec);
307
+ return self;
308
+ }
309
+
310
+ /* Computes the results of spline interpolation for the given set
311
+ of x points. It assumes that x points are sorted and within range ...
312
+ */
313
+ static void function_compute_spline_interpolation(long dat_size,
314
+ const double * x_dat,
315
+ const double * y_dat,
316
+ const double * y2_dat,
317
+ long dest_size,
318
+ const double * x,
319
+ double * y)
320
+ {
321
+ long low,hi,mid;
322
+ double h;
323
+ double a,b;
324
+ low = 0;
325
+ hi = dat_size - 1;
326
+ if(dest_size <= 1) /* nothing interesting to be done here...*/
327
+ return;
328
+ if(x[0] < x_dat[0] || x[dest_size - 1] > x_dat[dat_size - 1])
329
+ rb_raise(rb_eRuntimeError, "x range should be within x_dat range");
330
+ /* first, we seek the first point by bisection */
331
+ while(low - hi > 1)
332
+ {
333
+ mid = (low + hi) >> 1;
334
+ if(x[0] > x_dat[mid])
335
+ low = mid;
336
+ else
337
+ hi = mid;
338
+ }
339
+
340
+ for(hi = 0; hi < dest_size; hi++)
341
+ {
342
+ while(x_dat[low + 1] < x[hi] && low < dat_size - 1)
343
+ low++; /* seek forward - shouldn't be too long ? */
344
+ if(hi && x[hi] < x[hi - 1])
345
+ rb_raise(rb_eArgError,
346
+ "X values should be sorted");
347
+ h = x_dat[low + 1] - x_dat[low];
348
+ /* should hopefully not be zero */
349
+ if(h <= 0.0)
350
+ rb_raise(rb_eRuntimeError,
351
+ "x_dat must be strictly growing");
352
+ a = (x_dat[low + 1] - x[hi])/h;
353
+ b = - (x_dat[low] - x[hi])/h;
354
+ /* spline evaluation */
355
+ y[hi] = a * y_dat[low] +
356
+ b * y_dat[low + 1] +
357
+ ( (a*a*a - a) * y2_dat[low] +
358
+ (b*b*b - b) * y2_dat[low + 1]
359
+ ) * (h * h)/6.0;
360
+ }
361
+ }
362
+
363
+ /* makes sure that the spline data is present and up-to-date, refreshing
364
+ it if necessary
365
+ */
366
+ static void function_ensure_spline_data_present(VALUE self)
367
+ {
368
+ VALUE x_vec = get_x_vector(self);
369
+ VALUE y_vec = get_y_vector(self);
370
+ VALUE cache = get_spline_vector(self);
371
+ long dat_size = function_sanity_check(self);
372
+
373
+ if(! IS_A_DVECTOR(cache) ||
374
+ DVECTOR_IS_DIRTY(x_vec) ||
375
+ DVECTOR_IS_DIRTY(y_vec) ||
376
+ DVECTOR_SIZE(cache) == dat_size
377
+ )
378
+ function_compute_spline_data(self);
379
+ }
380
+
381
+ /* Interpolates the value of the function at the points given.
382
+ Returns a brand new Dvector. The X values must be sorted !
383
+ */
384
+ static VALUE function_compute_spline(VALUE self, VALUE x_values)
385
+ {
386
+ VALUE x_vec = get_x_vector(self);
387
+ VALUE y_vec = get_y_vector(self);
388
+ VALUE cache;
389
+ VALUE ret_val;
390
+ long dat_size = function_sanity_check(self);
391
+ long size = DVECTOR_SIZE(x_values);
392
+
393
+ function_ensure_spline_data_present(self);
394
+
395
+ cache = get_spline_vector(self);
396
+
397
+ ret_val = rb_funcall(cDvector, rb_intern("new"),
398
+ 1, LONG2NUM(size));
399
+ double * x_dat = Dvector_Data_for_Read(x_vec,NULL);
400
+ double * y_dat = Dvector_Data_for_Read(y_vec,NULL);
401
+ double * spline = Dvector_Data_for_Read(cache,NULL);
402
+ double * x = Dvector_Data_for_Read(x_values,NULL);
403
+ double * y = Dvector_Data_for_Write(ret_val,NULL);
404
+
405
+ function_compute_spline_interpolation(dat_size, x_dat,
406
+ y_dat, spline,
407
+ size, x, y);
408
+ return ret_val;
409
+ }
410
+
411
+ /*
412
+ Returns an interpolant that can be fed to
413
+ Special_Paths#append_interpolant_to_path
414
+ to make nice splines.
415
+
416
+ Can be used this way:
417
+
418
+ f = Function.new(x,y)
419
+ t.append_interpolant_to_path(f.make_interpolant)
420
+ t.stroke
421
+ */
422
+ static VALUE function_make_interpolant(VALUE self)
423
+ {
424
+ VALUE x_vec = get_x_vector(self);
425
+ VALUE y_vec = get_y_vector(self);
426
+ VALUE cache;
427
+ VALUE a_vec,b_vec,c_vec;
428
+ VALUE ret_val;
429
+ double *x, *y, *a, *b, *c, *y2;
430
+ double delta_x;
431
+ long size = function_sanity_check(self);
432
+ long i;
433
+
434
+ function_ensure_spline_data_present(self);
435
+
436
+ cache = get_spline_vector(self);
437
+ x = Dvector_Data_for_Read(x_vec,NULL);
438
+ y = Dvector_Data_for_Read(y_vec,NULL);
439
+ y2 = Dvector_Data_for_Read(cache,NULL);
440
+
441
+ a_vec = rb_funcall(cDvector, idNew, 1, LONG2NUM(size));
442
+ a = Dvector_Data_for_Write(a_vec, NULL);
443
+ b_vec = rb_funcall(cDvector, idNew, 1, LONG2NUM(size));
444
+ b = Dvector_Data_for_Write(b_vec, NULL);
445
+ c_vec = rb_funcall(cDvector, idNew, 1, LONG2NUM(size));
446
+ c = Dvector_Data_for_Write(c_vec, NULL);
447
+
448
+ /* from my computations, the formula is the following:
449
+ A = (y_2n+1 - y_2n)/(6 * delta_x)
450
+ B = 0.5 * y_2n
451
+ C = (y_n+1 - y_n)/delta_x - (2 * y_2n + y_2n+1) * delta_x/6
452
+ */
453
+
454
+ for(i = 0; i < size - 1; i++)
455
+ {
456
+ delta_x = x[i+1] - x[i];
457
+ a[i] = (y2[i+1] - y2[i]) / (6.0 * delta_x);
458
+ b[i] = 0.5 * y2[i];
459
+ c[i] = (y[i+1] - y[i])/delta_x -
460
+ (2 * y2[i] + y2[i+1]) * (delta_x / 6.0);
461
+ }
462
+ a[i] = b[i] = c[i] = 0.0;
463
+ ret_val = rb_ary_new();
464
+ rb_ary_push(ret_val, x_vec);
465
+ rb_ary_push(ret_val, y_vec);
466
+ rb_ary_push(ret_val, a_vec);
467
+ rb_ary_push(ret_val, b_vec);
468
+ rb_ary_push(ret_val, c_vec);
469
+
470
+ return ret_val;
471
+ }
472
+
473
+
474
+ /* the function fort joint sorting...*/
475
+ PRIVATE void joint_quicksort(double *const x_values, double * const y_values,
476
+ size_t total_elems);
477
+
478
+ /* Dvector's lock */
479
+ #define DVEC_TMPLOCK FL_USER1
480
+
481
+ /* call-seq:
482
+ Function.joint_sort(x,y)
483
+
484
+ Sorts +x+, while ensuring that the corresponding +y+ values
485
+ keep matching. Should be pretty fast, as it is derived from
486
+ glibc's quicksort.
487
+
488
+ a = Dvector[3,2,1]
489
+ b = a * 2 -> [6,4,2]
490
+ Function.joint_sort(a,b) -> [[1,2,3], [2,4,6]]
491
+ */
492
+
493
+ static VALUE function_joint_sort(VALUE self, VALUE x, VALUE y)
494
+ {
495
+ long x_len, y_len;
496
+ double * x_values = Dvector_Data_for_Write(x, &x_len);
497
+ double * y_values = Dvector_Data_for_Write(y, &y_len);
498
+ if(x_len != y_len)
499
+ rb_raise(rb_eArgError,"both vectors must have the same size");
500
+ else
501
+ {
502
+ /* we temporarily freeze both Dvectors before sorting */
503
+ FL_SET(x, DVEC_TMPLOCK);
504
+ FL_SET(y, DVEC_TMPLOCK);
505
+ joint_quicksort(x_values, y_values, (size_t) x_len);
506
+ /* and unfreeze them */
507
+ FL_UNSET(x, DVEC_TMPLOCK);
508
+ FL_UNSET(y, DVEC_TMPLOCK);
509
+ }
510
+ /* we return the array of both Dvectors */
511
+ return rb_ary_new3(2,x,y);
512
+ }
513
+
514
+
515
+ /* call-seq:
516
+ f.each do |x,y| _code_ end
517
+
518
+ Iterates over all the points in the Function, yielding X and Y for
519
+ each point.
520
+ */
521
+ static VALUE function_each(VALUE self) /* :yields: x,y */
522
+ {
523
+
524
+ long x_len, y_len;
525
+ VALUE x = get_x_vector(self);
526
+ VALUE y = get_y_vector(self);
527
+ double * x_values = Dvector_Data_for_Write(x, &x_len);
528
+ double * y_values = Dvector_Data_for_Write(y, &y_len);
529
+ if(x_len != y_len)
530
+ rb_raise(rb_eRuntimeError,"X and Y must have the same size");
531
+ else
532
+ {
533
+ /* we temporarily freeze both Dvectors during iteration */
534
+ FL_SET(x, DVEC_TMPLOCK);
535
+ FL_SET(y, DVEC_TMPLOCK);
536
+ while(x_len--)
537
+ {
538
+ VALUE flt_x = rb_float_new(*x_values++);
539
+ VALUE flt_y = rb_float_new(*y_values++);
540
+ rb_yield_values(2, flt_x, flt_y);
541
+ }
542
+ /* and unfreeze them */
543
+ FL_UNSET(x, DVEC_TMPLOCK);
544
+ FL_UNSET(y, DVEC_TMPLOCK);
545
+ }
546
+ return self; /* nothing interesting */
547
+
548
+ }
549
+
550
+ /*
551
+ Makes sure the function is sorted.
552
+ */
553
+ static VALUE function_ensure_sorted(VALUE self)
554
+ {
555
+ if(!RTEST(function_is_sorted(self)))
556
+ function_sort(self);
557
+ return self;
558
+ }
559
+
560
+
561
+
562
+ /*
563
+ call-seq:
564
+ interpolate(x_values)
565
+ interpolate(a_number)
566
+
567
+ Computes interpolated values of the data contained in +f+ and
568
+ returns a Function object holding both +x_values+ and the computed
569
+ Y values. +x_values+ will be sorted if necessary.
570
+
571
+ With the second form, specify only the number of points, and
572
+ the function will construct the appropriate vector with equally spaced
573
+ points within the function range.
574
+ */
575
+ static VALUE function_interpolate(VALUE self, VALUE x_values)
576
+ {
577
+ if(NUMERIC(x_values))
578
+ {
579
+ /* we're in the second case, although I sincerely doubt it would
580
+ come useful
581
+ */
582
+ long size,i;
583
+ /* we make sure the function is sorted */
584
+ function_ensure_sorted(self);
585
+ double * data;
586
+ double x_min;
587
+ double x_max;
588
+ data = Dvector_Data_for_Read(get_x_vector(self), &size);
589
+ x_min = *data;
590
+ x_max = *(data + size -1);
591
+ x_values = rb_funcall(cDvector, idNew, 1, x_values);
592
+ data = Dvector_Data_for_Write(x_values, &size);
593
+ for(i = 0;i < size; i++)
594
+ data[i] = x_min + ((x_max - x_min)/((double) (size-1))) * i;
595
+ }
596
+ if(! IS_A_DVECTOR(x_values))
597
+ rb_raise(rb_eArgError, "x_values should be a Dvector or a number");
598
+ else
599
+ {
600
+ /* sort x_values */
601
+ if(! dvector_is_sorted(x_values))
602
+ rb_funcall(x_values, idSort,0);
603
+ VALUE y_values = function_compute_spline(self, x_values);
604
+ return rb_funcall(cFunction, idNew, 2, x_values, y_values);
605
+ }
606
+ return Qnil;
607
+ }
608
+
609
+ /*
610
+ Strips all the points containing NaN values from the function, and
611
+ returns the number of points stripped.
612
+ */
613
+ static VALUE function_strip_nan(VALUE self)
614
+ {
615
+ long size = function_sanity_check(self);
616
+ long nb_stripped = 0;
617
+ long i;
618
+
619
+ double *x = Dvector_Data_for_Write(get_x_vector(self),NULL);
620
+ double *y = Dvector_Data_for_Write(get_y_vector(self),NULL);
621
+ for( i = 0; i < size; i++)
622
+ {
623
+ if(isnan(x[i]) || isnan(y[i]))
624
+ nb_stripped ++;
625
+ else
626
+ {
627
+ x[i - nb_stripped] = x[i];
628
+ y[i - nb_stripped] = y[i];
629
+ }
630
+ }
631
+ if(nb_stripped)
632
+ {
633
+ Dvector_Data_Resize(get_x_vector(self), size - nb_stripped);
634
+ Dvector_Data_Resize(get_y_vector(self), size - nb_stripped);
635
+ }
636
+ return INT2NUM(nb_stripped);
637
+ }
638
+
639
+ /*
640
+ Splits the function into monotonic sub-functions.
641
+ Returns the array of the subfunctions. The returned values are
642
+ necessarily new values.
643
+ */
644
+
645
+ static VALUE function_split_monotonic(VALUE self)
646
+ {
647
+ VALUE ret = rb_ary_new();
648
+ VALUE cur_x = Dvector_Create();
649
+ VALUE cur_y = Dvector_Create();
650
+
651
+ long size = function_sanity_check(self);
652
+ long i;
653
+ if(size < 2)
654
+ rb_raise(rb_eRuntimeError, "Function needs to have at least 2 points");
655
+
656
+ double *x = Dvector_Data_for_Read(get_x_vector(self),NULL);
657
+ double *y = Dvector_Data_for_Read(get_y_vector(self),NULL);
658
+
659
+ double last_x;
660
+ double direction; /* -1 if down, +1 if up, so that the product of
661
+ (x - last_x) with direction should always be positive
662
+ */
663
+ VALUE f;
664
+
665
+
666
+ /* bootstrap */
667
+ if(x[1] > x[0])
668
+ direction = 1;
669
+ else
670
+ direction = -1;
671
+ last_x = x[1];
672
+ for(i = 0; i < 2; i++)
673
+ {
674
+ Dvector_Push_Double(cur_x, x[i]);
675
+ Dvector_Push_Double(cur_y, y[i]);
676
+ }
677
+
678
+ for(i = 2; i < size; i++)
679
+ {
680
+ if(direction * (x[i] - last_x) < 0)
681
+ {
682
+ /* we need to add a new set of Dvectors */
683
+ f = Function_Create(cur_x, cur_y);
684
+ rb_ary_push(ret, f);
685
+ cur_x = Dvector_Create();
686
+ cur_y = Dvector_Create();
687
+ Dvector_Push_Double(cur_x, x[i-1]);
688
+ Dvector_Push_Double(cur_y, y[i-1]);
689
+ direction *= -1;
690
+ }
691
+ /* store the current point */
692
+ Dvector_Push_Double(cur_x, x[i]);
693
+ Dvector_Push_Double(cur_y, y[i]);
694
+ last_x = x[i];
695
+ }
696
+ f = Function_Create(cur_x, cur_y);
697
+ rb_ary_push(ret, f);
698
+ return ret;
699
+ }
700
+
701
+
702
+ /*
703
+ Sorts the X values while keeping the matching Y values.
704
+ */
705
+ static VALUE function_sort(VALUE self)
706
+ {
707
+ return function_joint_sort(self,get_x_vector(self), get_y_vector(self));
708
+ }
709
+
710
+ /*
711
+ Returns a Dvector with two elements: the X and Y values of the
712
+ point at the given index.
713
+ */
714
+ static VALUE function_point(VALUE self, VALUE index)
715
+ {
716
+ if(! NUMERIC(index))
717
+ rb_raise(rb_eArgError, "index has to be numeric");
718
+ else
719
+ {
720
+ long i = NUM2LONG(index);
721
+ long size = function_sanity_check(self);
722
+ if(size > 0 && i < size)
723
+ {
724
+ VALUE point = rb_funcall(cDvector, idNew, 1, INT2NUM(2));
725
+ double * dat = Dvector_Data_for_Write(point, NULL);
726
+ double *x = Dvector_Data_for_Read(get_x_vector(self),NULL);
727
+ double *y = Dvector_Data_for_Read(get_y_vector(self),NULL);
728
+ dat[0] = x[i];
729
+ dat[1] = y[i];
730
+ return point;
731
+ }
732
+ else
733
+ return Qnil;
734
+ }
735
+ return Qnil;
736
+ }
737
+
738
+ static void init_IDs()
739
+ {
740
+ idSize = rb_intern("size");
741
+ idSetDirty = rb_intern("dirty=");
742
+ idDirty = rb_intern("dirty?");
743
+ idSort = rb_intern("sort");
744
+ idNew = rb_intern("new");
745
+ }
746
+
747
+
748
+ /* a smaller helper for the following function */
749
+ #define DISTANCE(x,y) (((x) - xpoint) * ((x) - xpoint) /xscale/xscale \
750
+ + ((y) - ypoint) * ((y) - ypoint) /yscale/yscale)
751
+
752
+ /*
753
+ Returns the distance of a point to the function, computed by the minimum
754
+ of ((x - xpoint)/xscale)**2 + ((y - ypoint)/yscale)**2. If index
755
+ is not NULL, it receives the index of the point of minimum distance.
756
+ */
757
+ static double private_function_distance(VALUE self,
758
+ double xpoint, double ypoint,
759
+ double xscale, double yscale,
760
+ long * dest_index)
761
+ {
762
+ long size = function_sanity_check(self);
763
+ const double *x = Dvector_Data_for_Read(get_x_vector(self),NULL);
764
+ const double *y = Dvector_Data_for_Read(get_y_vector(self),NULL);
765
+ double min = DISTANCE(x[0],y[0]);
766
+ double cur;
767
+ long index = 0;
768
+ long i;
769
+ for(i = 1; i < size; i++)
770
+ {
771
+ cur = DISTANCE(x[i], y[i]);
772
+ if(cur < min)
773
+ {
774
+ index = i;
775
+ min = cur;
776
+ }
777
+ }
778
+ if(dest_index)
779
+ *dest_index = index;
780
+ return sqrt(min);
781
+ }
782
+
783
+ /*
784
+ call-seq:
785
+ f.distance(x,y) -> a_number
786
+ f.distance(x,y, xscale, yscale) -> a_number
787
+
788
+ Returns the distance of the function to the given point. Optionnal
789
+ xscale and yscale says by how much we should divide the x and y
790
+ coordinates before computing the distance. Use it if the distance is not
791
+ homogeneous.
792
+ */
793
+
794
+ static VALUE function_distance(int argc, VALUE *argv, VALUE self)
795
+ {
796
+ switch(argc)
797
+ {
798
+ case 2:
799
+ return rb_float_new(private_function_distance(self,
800
+ NUM2DBL(argv[0]),
801
+ NUM2DBL(argv[1]),
802
+ 1.0,1.0,NULL));
803
+ case 4:
804
+ return rb_float_new(private_function_distance(self,
805
+ NUM2DBL(argv[0]),
806
+ NUM2DBL(argv[1]),
807
+ NUM2DBL(argv[2]),
808
+ NUM2DBL(argv[3]),
809
+ NULL));
810
+ default:
811
+ rb_raise(rb_eArgError, "distance should have 2 or 4 parameters");
812
+ }
813
+ return Qnil;
814
+ }
815
+
816
+
817
+ /*
818
+ Code for integration.
819
+ */
820
+ static double private_function_integrate(VALUE self, long start, long end)
821
+ {
822
+ long size = function_sanity_check(self);
823
+ const double *x = Dvector_Data_for_Read(get_x_vector(self),NULL);
824
+ const double *y = Dvector_Data_for_Read(get_y_vector(self),NULL);
825
+ long i = start;
826
+ double val = 0;
827
+ if(end >= size)
828
+ end = size - 1;
829
+ if(start < 0)
830
+ start = 0;
831
+ while(i < (end))
832
+ {
833
+ val += (y[i] + y[i+1]) * (x[i+1] - x[i]) * 0.5;
834
+ i++;
835
+ }
836
+ return val;
837
+ }
838
+
839
+ /*
840
+ :call-seq:
841
+ f.integrate() -> value
842
+ f.integrate(start_index, end_index) -> value
843
+
844
+ Returns the value of the integral of the function between the
845
+ two indexes given, or over the whole function if no indexes are
846
+ specified.
847
+ */
848
+ static VALUE function_integrate(int argc, VALUE *argv, VALUE self)
849
+ {
850
+ long start,end;
851
+ switch(argc)
852
+ {
853
+ case 0:
854
+ start = 0;
855
+ end = function_sanity_check(self) - 1;
856
+ break;
857
+ case 2:
858
+ start = NUM2LONG(argv[0]);
859
+ end = NUM2LONG(argv[1]);
860
+ break;
861
+ default:
862
+ rb_raise(rb_eArgError, "integrate should have 0 or 2 parameters");
863
+ }
864
+ return rb_float_new(private_function_integrate(self,start,end));
865
+ }
866
+
867
+ /*
868
+ Computes the primitive of the Function and returns it as a new Function.
869
+ The newly created function shares the X vector with the previous one.
870
+ */
871
+ static VALUE function_primitive(VALUE self)
872
+ {
873
+ long size = function_sanity_check(self);
874
+ const double *x = Dvector_Data_for_Read(get_x_vector(self),NULL);
875
+ const double *y = Dvector_Data_for_Read(get_y_vector(self),NULL);
876
+ VALUE primitive = Dvector_Create();
877
+ long i = 0;
878
+ double val = 0;
879
+ while(i < (size - 1))
880
+ {
881
+ Dvector_Push_Double(primitive, val);
882
+ val += (y[i] + y[i+1]) * (x[i+1] - x[i]) * 0.5;
883
+ i++;
884
+ }
885
+ Dvector_Push_Double(primitive, val);
886
+ return Function_Create(get_x_vector(self), primitive);
887
+ }
888
+
889
+ /*
890
+ Returns the number of points inside the function.
891
+ */
892
+ static VALUE function_size(VALUE self)
893
+ {
894
+ long size = function_sanity_check(self);
895
+ return LONG2NUM(size);
896
+ }
897
+
898
+ /*
899
+ Document-class: Dobjects::Function
900
+
901
+ Function is a class that embeds two Dvectors, one for X data and one for Y
902
+ data. It provides
903
+
904
+ - facilities for sorting the X while keeping the Y matching, with #sort and
905
+ Function.joint_sort;
906
+ - to check if X data is sorted: #sorted?, #is_sorted;
907
+ - interpolation, with #compute_spline, #compute_spline_data and #interpolate;
908
+ - some functions for data access : #x, #y, #point;
909
+ - some utiliy functions: #split_monotonic, #strip_nan;
910
+ - data inspection: #min, #max;
911
+ - some computationnal functions: #integrate, #primitive.
912
+
913
+ And getting bigger everyday...
914
+ */
915
+ void Init_Function()
916
+ {
917
+ init_IDs();
918
+
919
+ rb_require("Dobjects/Dvector");
920
+ VALUE mDobjects = rb_define_module("Dobjects");
921
+ cFunction = rb_define_class_under(mDobjects, "Function", rb_cObject);
922
+
923
+ /* get the Dvector class */
924
+ cDvector = rb_const_get(mDobjects, rb_intern("Dvector"));
925
+
926
+ rb_define_method(cFunction, "initialize", function_initialize, 2);
927
+ rb_define_method(cFunction, "sorted?", function_is_sorted, 0);
928
+ rb_define_alias(cFunction, "is_sorted", "sorted?");
929
+
930
+ rb_define_singleton_method(cFunction, "joint_sort", function_joint_sort, 2);
931
+ rb_define_method(cFunction, "sort", function_sort, 0);
932
+
933
+ /* spline stuff :*/
934
+ rb_define_method(cFunction, "compute_spline_data",
935
+ function_compute_spline_data, 0);
936
+ rb_define_method(cFunction, "compute_spline",
937
+ function_compute_spline, 1);
938
+
939
+ rb_define_method(cFunction, "interpolate",
940
+ function_interpolate, 1);
941
+ rb_define_method(cFunction, "make_interpolant",
942
+ function_make_interpolant, 0);
943
+
944
+ /* access to data */
945
+ rb_define_method(cFunction, "point", function_point, 1);
946
+ rb_define_method(cFunction, "x", get_x_vector, 0);
947
+ rb_define_method(cFunction, "y", get_y_vector, 0);
948
+
949
+
950
+ rb_define_method(cFunction, "size", function_size, 0);
951
+ rb_define_alias(cFunction, "length", "size");
952
+
953
+
954
+ /* iterator */
955
+ rb_define_method(cFunction, "each",
956
+ function_each, 0);
957
+
958
+ /* stripping of NaNs */
959
+ rb_define_method(cFunction, "strip_nan", function_strip_nan, 0);
960
+
961
+ /* split into monotonic subfunctions */
962
+ rb_define_method(cFunction, "split_monotonic", function_split_monotonic, 0);
963
+
964
+ /* integration between two integer boundaries */
965
+ rb_define_method(cFunction, "integrate", function_integrate, -1);
966
+ /* primitive */
967
+ rb_define_method(cFunction, "primitive", function_primitive, 0);
968
+
969
+ /* distance to a point */
970
+ rb_define_method(cFunction, "distance", function_distance, -1);
971
+
972
+
973
+ /* a few more methods better written in pure Ruby */
974
+ rb_require("Dobjects/Function_extras.rb");
975
+
976
+ /* now, we import the necessary symbols from Dvector */
977
+ RB_IMPORT_SYMBOL(cDvector, Dvector_Data_for_Read);
978
+ RB_IMPORT_SYMBOL(cDvector, Dvector_Data_for_Write);
979
+ RB_IMPORT_SYMBOL(cDvector, Dvector_Data_Resize);
980
+ RB_IMPORT_SYMBOL(cDvector, Dvector_Create);
981
+ RB_IMPORT_SYMBOL(cDvector, Dvector_Push_Double);
982
+ }
983
+
984
+ IMPLEMENT_SYMBOL(Dvector_Data_for_Read);
985
+ IMPLEMENT_SYMBOL(Dvector_Data_for_Write);
986
+ IMPLEMENT_SYMBOL(Dvector_Data_Resize);
987
+ IMPLEMENT_SYMBOL(Dvector_Create);
988
+ IMPLEMENT_SYMBOL(Dvector_Push_Double);