weft-qda 0.9.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/lib/weft.rb +21 -0
  2. data/lib/weft/WEFT-VERSION-STRING.rb +1 -0
  3. data/lib/weft/application.rb +130 -0
  4. data/lib/weft/backend.rb +39 -0
  5. data/lib/weft/backend/marshal.rb +26 -0
  6. data/lib/weft/backend/mysql.rb +267 -0
  7. data/lib/weft/backend/n6.rb +366 -0
  8. data/lib/weft/backend/sqlite.rb +633 -0
  9. data/lib/weft/backend/sqlite/category_tree.rb +104 -0
  10. data/lib/weft/backend/sqlite/schema.rb +152 -0
  11. data/lib/weft/backend/sqlite/upgradeable.rb +55 -0
  12. data/lib/weft/category.rb +157 -0
  13. data/lib/weft/coding.rb +355 -0
  14. data/lib/weft/document.rb +118 -0
  15. data/lib/weft/filters.rb +243 -0
  16. data/lib/weft/wxgui.rb +687 -0
  17. data/lib/weft/wxgui/category.xpm +26 -0
  18. data/lib/weft/wxgui/dialogs.rb +128 -0
  19. data/lib/weft/wxgui/document.xpm +25 -0
  20. data/lib/weft/wxgui/error_handler.rb +52 -0
  21. data/lib/weft/wxgui/inspectors.rb +361 -0
  22. data/lib/weft/wxgui/inspectors/category.rb +165 -0
  23. data/lib/weft/wxgui/inspectors/codereview.rb +275 -0
  24. data/lib/weft/wxgui/inspectors/document.rb +139 -0
  25. data/lib/weft/wxgui/inspectors/imagedocument.rb +56 -0
  26. data/lib/weft/wxgui/inspectors/script.rb +35 -0
  27. data/lib/weft/wxgui/inspectors/search.rb +265 -0
  28. data/lib/weft/wxgui/inspectors/textcontrols.rb +304 -0
  29. data/lib/weft/wxgui/lang.rb +17 -0
  30. data/lib/weft/wxgui/lang/en.rb +45 -0
  31. data/lib/weft/wxgui/mondrian.xpm +44 -0
  32. data/lib/weft/wxgui/search.xpm +25 -0
  33. data/lib/weft/wxgui/sidebar.rb +498 -0
  34. data/lib/weft/wxgui/utilities.rb +148 -0
  35. data/lib/weft/wxgui/weft16.xpm +31 -0
  36. data/lib/weft/wxgui/workarea.rb +249 -0
  37. data/test/001-document.rb +196 -0
  38. data/test/002-category.rb +138 -0
  39. data/test/003-code.rb +370 -0
  40. data/test/004-application.rb +52 -0
  41. data/test/006-filters.rb +139 -0
  42. data/test/009a-backend_sqlite_basic.rb +280 -0
  43. data/test/009b-backend_sqlite_complex.rb +175 -0
  44. data/test/009c_backend_sqlite_bench.rb +81 -0
  45. data/test/010-backend_nudist.rb +5 -0
  46. data/test/all-tests.rb +1 -0
  47. data/test/manual-gui-script.txt +24 -0
  48. data/test/testdata/autocoding-test.txt +15 -0
  49. data/test/testdata/iso-8859-1.txt +5 -0
  50. data/test/testdata/sample_doc.txt +19 -0
  51. data/test/testdata/search_results.txt +1254 -0
  52. data/test/testdata/text1-dos-ascii.txt +2 -0
  53. data/test/testdata/text1-unix-utf8.txt +2 -0
  54. data/weft-qda.rb +28 -0
  55. metadata +96 -0
@@ -0,0 +1,138 @@
1
+ require 'english'
2
+
3
+ $:.push('../lib/')
4
+
5
+ require 'weft/category'
6
+ require 'test/unit'
7
+
8
+ class TestDocument < Test::Unit::TestCase
9
+ def setup
10
+
11
+ end
12
+
13
+ def test_basic
14
+ c = QDA::Category.new('The Title',nil)
15
+ assert_equal('The Title', c.name,
16
+ 'Set title at initialisation')
17
+ assert_equal(0, c.children.length,
18
+ 'Empty at initialisation')
19
+
20
+ c2 = QDA::Category.new('Child Node',c)
21
+ assert_equal(1, c.children.length,
22
+ 'Added a child')
23
+ end
24
+
25
+ def test_memo
26
+ c = QDA::Category.new('The Title',nil, 'My Memo')
27
+ assert_equal('My Memo', c.memo,
28
+ 'Set memo at initialisation')
29
+
30
+ c2 = QDA::Category.new('Child Node',c)
31
+ assert_equal('', c2.memo,
32
+ 'Default memo = ""')
33
+
34
+ c2.memo = 'Foo'
35
+ assert_equal('Foo', c2.memo,
36
+ 'Set memo with accessor')
37
+ end
38
+
39
+ def test_code
40
+ a = QDA::Code.new(1, 0, 5)
41
+ assert( a.include?(0), "Code#include?")
42
+ assert( a.include?(1), "Code#include?")
43
+ assert( a.include?(4), "Code#include?")
44
+ assert_equal(false, a.include?(5), "Code#include?")
45
+ assert_equal(false, a.include?(9), "Code#include?")
46
+ end
47
+
48
+ def test_do_code
49
+ c = QDA::Category.new('The Title',nil)
50
+ # create an initial code
51
+ c.code(2, 0, 20)
52
+ assert_equal(1, c.num_of_docs,
53
+ 'Have coded one document')
54
+ assert_equal(1, c.num_of_codes,
55
+ 'Have added one code')
56
+ assert_equal(0, c.codes[2][0].offset,
57
+ 'Have set code offset')
58
+ assert_equal(20, c.codes[2][0].length,
59
+ 'Have set code length')
60
+ assert_equal(20, c.num_of_chars,
61
+ 'Have calculated total chars')
62
+
63
+ # create a separate code of the same document
64
+ c.code(2, 90, 10)
65
+ assert_equal(1, c.num_of_docs,
66
+ 'Have still coded one document')
67
+ assert_equal(2, c.num_of_codes,
68
+ 'Have added one code')
69
+ assert_equal(30, c.num_of_chars,
70
+ 'Have increased total chars')
71
+
72
+ # code a region which partiall overlaps and extends the first code
73
+ c.code(2, 10, 15)
74
+ assert_equal(1, c.num_of_docs,
75
+ 'Have done joint coding')
76
+ assert_equal(2, c.num_of_codes,
77
+ 'Have done joint coding')
78
+ assert_kind_of(QDA::Code, c.codes[2][0],
79
+ 'Have done joint one code')
80
+ assert_equal(0, c.codes[2][0].offset,
81
+ 'Have updated code offset')
82
+ assert_equal(25, c.codes[2][0].length,
83
+ 'Have updated code length')
84
+ assert_equal(35, c.num_of_chars,
85
+ 'Have increased total chars')
86
+
87
+ # code another region that is distinct and between the first two
88
+ c.code(2, 30, 30)
89
+ assert_equal(1, c.num_of_docs,
90
+ 'Have done discrete coding')
91
+ assert_equal(3, c.num_of_codes,
92
+ 'Have done discrete coding')
93
+ assert_kind_of(QDA::Code, c.codes[2][1],
94
+ 'Have done discrete one code')
95
+ assert_equal(30, c.codes[2][1].offset,
96
+ 'Have updated code offset')
97
+ assert_equal(30, c.codes[2][1].length,
98
+ 'Have updated code length')
99
+ assert_equal(65, c.num_of_chars,
100
+ 'Have increased total chars')
101
+
102
+ # code another document
103
+ c.code(1, 120, 100)
104
+ assert_equal(2, c.num_of_docs,
105
+ 'Have increased documents with new document')
106
+ assert_equal(4, c.num_of_codes,
107
+ 'Have increased codes with new document')
108
+
109
+ # uncode a portion of first code
110
+ c.uncode(2, 5, 15)
111
+ assert_equal(2, c.num_of_docs,
112
+ 'Have done split uncoding')
113
+ assert_equal(4, c.codes[2].length,
114
+ 'Have done split uncoding')
115
+ assert_kind_of(QDA::Code, c.codes[2][0],
116
+ 'Have done split uncode')
117
+ assert_equal(0, c.codes[2][0].offset,
118
+ 'Have updated code offset')
119
+ assert_equal(5, c.codes[2][0].length,
120
+ 'Have updated code length')
121
+
122
+ assert_kind_of(QDA::Code, c.codes[2][1],
123
+ 'Have done split uncode')
124
+ assert_equal(20, c.codes[2][1].offset,
125
+ 'Have updated code offset')
126
+ assert_equal(5, c.codes[2][1].length,
127
+ 'Have updated code length')
128
+
129
+ assert_kind_of(QDA::Code, c.codes[2][2],
130
+ 'Have done split uncode')
131
+ assert_equal(30, c.codes[2][2].offset,
132
+ 'Have updated code offset')
133
+ assert_equal(30, c.codes[2][2].length,
134
+ 'Have updated code length')
135
+
136
+ # more tests ? ... seems to be all OK, not
137
+ end
138
+ end
data/test/003-code.rb ADDED
@@ -0,0 +1,370 @@
1
+ require 'english'
2
+
3
+ $:.push('../lib/')
4
+
5
+ require 'weft'
6
+ require 'test/unit'
7
+
8
+ class TestCode < Test::Unit::TestCase
9
+ include QDA
10
+
11
+ def setup
12
+
13
+ end
14
+
15
+ def test_basic
16
+ c_1 = Code.new(3, 0, 4)
17
+ assert_equal(3, c_1.docid,
18
+ 'Set docid at initialization')
19
+ assert_equal(0, c_1.offset,
20
+ 'Set offset at initialization')
21
+ assert_equal(4, c_1.length,
22
+ 'Set length at initialization')
23
+
24
+ c_1 << Code.new(3, 4, 5)
25
+ assert_equal(0, c_1.offset,
26
+ '<< other')
27
+ assert_equal(9, c_1.length,
28
+ '<< other.length')
29
+
30
+ end
31
+
32
+ def test_basic_codeset
33
+ # this shouldn work
34
+ q = CodeSet[ '11' ]
35
+ assert_kind_of(CodeSet, q)
36
+
37
+ assert_raises(ArgumentError) { CodeSet.new([ '11' ]) }
38
+ # boring constructor
39
+ q = CodeSet.new [ [4, 5, 6] ]
40
+ assert_equal(1, q.length)
41
+ assert_kind_of(Code, q[0])
42
+ c = q[0]
43
+ assert_equal(4, c.docid)
44
+ assert_equal(5, c.offset)
45
+ assert_equal(6, c.length)
46
+ end
47
+
48
+ def test_addition
49
+ c_1 = Code.new(3, 0, 3)
50
+ c_2 = Code.new(3, 2, 4)
51
+ c_3 = c_1 + c_2
52
+ assert_equal(6, c_3.length,
53
+ 'Overlapping addition')
54
+ assert_equal(0, c_3.offset,
55
+ 'Overlapping offset of first element')
56
+
57
+ c_1 = Code.new(3, 2, 6)
58
+ c_2 = Code.new(3, 2, 6)
59
+ c_3 = c_1 + c_2
60
+ assert_equal(6, c_3.length,
61
+ 'Identical addition')
62
+ assert_equal(2, c_3.offset,
63
+ 'Identical addition offset of first element')
64
+
65
+ c_1 = Code.new(3, 2, 6)
66
+ c_2 = Code.new(3, 2, 4)
67
+ c_3 = c_1 + c_2
68
+ assert_equal(6, c_3.length,
69
+ 'Identical addition')
70
+ assert_equal(2, c_3.offset,
71
+ 'Identical addition offset of first element')
72
+
73
+
74
+
75
+ c_1 = Code.new(3, 0, 2)
76
+ c_2 = Code.new(3, 4, 4)
77
+ c_3 = c_1 + c_2
78
+ assert_equal(2, c_3.length,
79
+ 'Disjoint addition')
80
+ assert_equal(0, c_3[0].offset,
81
+ 'Disjoint addition first element offset')
82
+ assert_equal(2, c_3[0].length,
83
+ 'Disjoint addition first element length')
84
+ assert_equal(4, c_3[1].offset,
85
+ 'Disjoint addition second element offset')
86
+ assert_equal(4, c_3[1].length,
87
+ 'Disjoint addition second element length')
88
+
89
+ c_1 = Code.new(3, 0, 2)
90
+ c_2 = Code.new(3, 4, 4)
91
+ c_3 = c_2 + c_1
92
+ assert_equal(2, c_3.length,
93
+ 'Disjoint addition')
94
+ assert_equal(0, c_3[0].offset,
95
+ 'Disjoint addition first element offset')
96
+ assert_equal(2, c_3[0].length,
97
+ 'Disjoint addition first element length')
98
+ assert_equal(4, c_3[1].offset,
99
+ 'Disjoint addition second element offset')
100
+ assert_equal(4, c_3[1].length,
101
+ 'Disjoint addition second element length')
102
+
103
+ end
104
+
105
+
106
+ def test_comparison
107
+ c_1 = Code.new(3, 0, 7)
108
+ c_2 = Code.new(3, 0, 7)
109
+ c_3 = Code.new(3, 5, 7)
110
+ c_4 = Code.new(3, 0, 19)
111
+ assert(c_1 == c_1, 'Identity Equality')
112
+ assert(c_1 == c_2, 'Equivalence Equality')
113
+ assert(c_1 != c_3, 'Offset inequality')
114
+ assert(c_1 != c_4, 'Length inequality')
115
+
116
+ c_1 = Code.new(3, 0, 7)
117
+ c_2 = Code.new(3, 2, 9)
118
+ assert(c_1.overlap?(c_2), 'Overlap 1/2')
119
+ assert(c_2.overlap?(c_1), 'Overlap 1/1')
120
+ assert(c_1.touch?(c_2), 'Touch 1/1')
121
+ assert(c_2.touch?(c_1), 'Touch 1/2')
122
+
123
+ c_1 = Code.new(3, 0, 7)
124
+ c_2 = Code.new(3, 7, 9)
125
+ assert(! c_1.overlap?(c_2), 'Overlap 2/2')
126
+ assert(! c_2.overlap?(c_1), 'Overlap 2/1')
127
+ assert(c_1.touch?(c_2), 'Touch 2/1')
128
+ assert(c_2.touch?(c_1), 'Touch 2/2')
129
+
130
+ c_1 = Code.new(3, 0, 7)
131
+ c_2 = Code.new(3, 8, 9)
132
+ assert(! c_1.overlap?(c_2), 'Overlap 3/2')
133
+ assert(! c_2.overlap?(c_1), 'Overlap 3/1')
134
+ assert(! c_1.touch?(c_2), 'Touch 3/1')
135
+ assert(! c_2.touch?(c_1), 'Touch 3/2')
136
+
137
+ end
138
+
139
+ def test_subtraction
140
+ c_1 = Code.new(3, 0, 7)
141
+ c_2 = Code.new(3, 2, 2)
142
+ c_3 = c_1 - c_2
143
+ assert_equal(2, c_3.length,
144
+ 'Disjoint subtraction')
145
+ assert_equal(0, c_3[0].offset,
146
+ 'Disjoint subtraction first element offset')
147
+ assert_equal(2, c_3[0].length,
148
+ 'Disjoint subtraction first element length')
149
+ assert_equal(4, c_3[1].offset,
150
+ 'Disjoint subtraction second element offset')
151
+ assert_equal(3, c_3[1].length,
152
+ 'Disjoint subtraction second element length')
153
+
154
+ c_1 = Code.new(3, 0, 4)
155
+ c_2 = Code.new(3, 2, 2)
156
+ c_3 = c_1 - c_2
157
+ assert_equal(1, c_3.length,
158
+ 'Tail subtraction')
159
+ assert_equal(0, c_3[0].offset,
160
+ 'Tail subtraction first element offset')
161
+ assert_equal(2, c_3[0].length,
162
+ 'Tail subtraction first element length')
163
+
164
+ c_1 = Code.new(3, 2, 4)
165
+ c_2 = Code.new(3, 0, 4)
166
+ c_3 = c_1 - c_2
167
+ assert_equal(1, c_3.length,
168
+ 'Head subtraction')
169
+ assert_equal(4, c_3[0].offset,
170
+ 'Tail subtraction first element offset')
171
+ assert_equal(2, c_3[0].length,
172
+ 'Tail subtraction first element length')
173
+
174
+ c_1 = Code.new(3, 12, 10)
175
+ c_2 = Code.new(3, 10, 20)
176
+ c_3 = c_1 - c_2
177
+ assert_equal(0, c_3.length,
178
+ 'Erase subtraction')
179
+
180
+ c_1 = Code.new(3, 5, 5)
181
+ c_2 = Code.new(3, 10, 20)
182
+ c_3 = c_1 - c_2
183
+ assert_equal(1, c_3.length,
184
+ 'Null subtraction')
185
+ assert_equal(5, c_3[0].offset,
186
+ 'Null subtraction first element offset')
187
+ assert_equal(5, c_3[0].length,
188
+ 'Null subtraction first element length')
189
+
190
+ c_1 = Code.new(3, 30, 30)
191
+ c_2 = Code.new(3, 5, 15)
192
+ c_3 = c_1 - c_2
193
+ assert_equal(1, c_3.length,
194
+ 'Null subtraction')
195
+ assert_equal(30, c_3[0].offset,
196
+ 'Null subtraction first element offset')
197
+ assert_equal(30, c_3[0].length,
198
+ 'Null subtraction first element length')
199
+
200
+ end
201
+
202
+ def test_intersection
203
+ c_1 = Code.new(3, 0, 7)
204
+ c_2 = Code.new(3, 2, 2)
205
+ c_3 = c_1 % c_2
206
+ assert_equal(2, c_3.length,
207
+ 'Island intersection')
208
+ assert_equal(2, c_3.offset,
209
+ 'Island intersection')
210
+
211
+ c_1 = Code.new(3, 5, 8)
212
+ c_2 = Code.new(3, 8, 20)
213
+ c_3 = c_1 % c_2
214
+ assert_equal(5, c_3.length,
215
+ 'Overlap intersection')
216
+ assert_equal(8, c_3.offset,
217
+ 'overlap intersection')
218
+
219
+ c_1 = Code.new(3, 0, 5)
220
+ c_2 = Code.new(3, 10, 5)
221
+ c_3 = c_1 % c_2
222
+ assert_nil(c_3, 'Disjoint intersection')
223
+
224
+ c_1 = Code.new(3, 0, 5)
225
+ c_2 = Code.new(3, 5, 10)
226
+ c_3 = c_1 % c_2
227
+ assert_nil(c_3, 'Adjacent intersection')
228
+ end
229
+
230
+ def test_set_intersect
231
+ cs_2 = CodeSet[ Code.new(3, 0, 5),
232
+ Code.new(3, 10, 10) ]
233
+
234
+ cs_1 = CodeSet[ Code.new(3, 3, 9) ]
235
+
236
+ intersect = cs_1.intersect(cs_2)
237
+
238
+ assert_equal(Code.new(3, 3, 2), intersect[0],
239
+ 'Overlap intersection')
240
+
241
+ assert_equal(Code.new(3, 10, 2), intersect[1],
242
+ 'Overlap intersection')
243
+
244
+ cs_2 = CodeSet[ Code.new(3, 2, 7) ]
245
+ cs_1 = CodeSet[ Code.new(3, 9, 5) ]
246
+
247
+ intersect = cs_1.intersect(cs_2)
248
+ assert([], 'Adjacent intersection')
249
+
250
+ cs_2 = CodeSet[ Code.new(3, 0, 4) ]
251
+ cs_1 = CodeSet[ Code.new(3, 9, 5) ]
252
+ intersect = cs_1.intersect(cs_2)
253
+ assert([], 'Disjoint intersection')
254
+ end
255
+
256
+ def test_set_union
257
+ cs_2 = CodeSet[ Code.new(3, 0, 5),
258
+ Code.new(3, 10, 10)]
259
+
260
+ cs_1 = CodeSet[ Code.new(3, 3, 9) ]
261
+
262
+ union = cs_1.union(cs_2)
263
+ assert_equal(Code.new(3, 0, 20), union[0],
264
+ 'Overlap set union')
265
+
266
+ cs_2 = CodeSet[ Code.new(3, 2, 7) ]
267
+ cs_1 = CodeSet[ Code.new(3, 9, 5) ]
268
+ union = cs_1.union(cs_2)
269
+
270
+ assert_equal(Code.new(3, 2, 12), union[0],
271
+ 'Adjacent set union')
272
+
273
+ cs_2 = CodeSet[ Code.new(3, 0, 4), Code.new(3, 20, 8) ]
274
+ cs_1 = CodeSet[ Code.new(3, 9, 5) ]
275
+ union = cs_1.union(cs_2)
276
+
277
+ assert_equal(Code.new(3, 0, 4), union[0],
278
+ 'Disjoint set union')
279
+ assert_equal(Code.new(3, 9, 5), union[1],
280
+ 'Disjoint set union')
281
+ assert_equal(Code.new(3, 20, 8), union[2],
282
+ 'Disjoint set union')
283
+ end
284
+
285
+ def test_set_exclude
286
+ cs_2 = CodeSet[ Code.new(3, 0, 5), Code.new(3, 10, 10) ]
287
+ cs_1 = CodeSet[ Code.new(3, 3, 9), Code.new(3, 25, 5) ]
288
+
289
+ exclusion = cs_2.exclude(cs_1)
290
+ assert_equal(Code.new(3, 0, 3), exclusion[0],
291
+ 'Overlap set exclusion')
292
+ assert_equal(Code.new(3, 12, 8), exclusion[1],
293
+ 'Overlap set exclusion')
294
+
295
+ exclusion = cs_1.exclude(cs_2)
296
+ assert_equal(Code.new(3, 5, 5), exclusion[0],
297
+ 'Overlap set exclusion')
298
+ assert_equal(Code.new(3, 25, 5), exclusion[1],
299
+ 'Overlap set exclusion')
300
+
301
+ cs_2 = CodeSet[ Code.new(3, 2, 7) ]
302
+ cs_1 = CodeSet[ Code.new(3, 9, 5) ]
303
+
304
+
305
+ exclusion = cs_2.exclude(cs_1)
306
+ assert_equal(Code.new(3, 2, 7), exclusion[0],
307
+ 'Adjacent set exclusion')
308
+
309
+ cs_2 = CodeSet[ Code.new(3, 0, 4), Code.new(3, 22, 8) ]
310
+ cs_1 = CodeSet[ Code.new(3, 9, 5) ]
311
+
312
+ exclusion = cs_2.exclude(cs_1)
313
+
314
+ assert_equal(Code.new(3, 0, 4), exclusion[0],
315
+ 'Disjoint set exclusion')
316
+ assert_equal(Code.new(3, 22, 8), exclusion[1],
317
+ 'Disjoint set exclusion')
318
+ end
319
+
320
+ def test_code_plus_fragment
321
+ fs = CodeSet[ Fragment.new('weft is nice', 'title', 5, 1) ]
322
+ cs = CodeSet[ Code.new(1, 13, 8) ]
323
+
324
+ isect = fs.intersect(cs)
325
+ first = isect[0]
326
+ assert_kind_of(Code, first)
327
+ assert_equal(13, first.offset)
328
+ assert_equal(4, first.length)
329
+ assert_equal(1, first.docid)
330
+ end
331
+
332
+ def test_codetable
333
+ tbl = CodingTable.new()
334
+ tbl.add( Code.new(3, 5, 6) )
335
+ assert_equal(1, tbl.num_of_docs)
336
+ assert_equal(6, tbl.num_of_chars)
337
+ assert_equal(1, tbl.num_of_codes)
338
+ end
339
+
340
+ def test_fragment_table
341
+ ft = FragmentTable.new()
342
+ ft.add( Fragment.new('the text', 'title', 6, 2) )
343
+
344
+ assert_equal(1, ft.num_of_docs)
345
+ assert_equal(8, ft.num_of_chars)
346
+ assert_equal(1, ft.num_of_codes)
347
+ ft.each_title do | title, codes |
348
+ assert_equal('title', title)
349
+ assert_equal(1, codes.length)
350
+ end
351
+
352
+
353
+ ct = ft.to_codingtable()
354
+ assert_equal(1, ct.num_of_docs)
355
+ assert_equal(8, ct.num_of_chars)
356
+ assert_equal(1, ct.num_of_codes)
357
+ assert_equal(Code, ct[2][0].class)
358
+ end
359
+
360
+ def test_codetable_plus_fragmenttable
361
+ ft = FragmentTable.new()
362
+ ft.add( Fragment.new('the text', 'title', 6, 2) )
363
+ ct = CodingTable.new()
364
+ ct.add( Code.new(2, 8, 10) )
365
+
366
+ out = ft.dup
367
+ ft.merge(ct)
368
+ ft.add( Fragment.new('something', 'xxx', 10, 1) )
369
+ end
370
+ end