htslib 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,102 +1,691 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "sam_funcs"
4
-
5
3
  module HTS
6
4
  module LibHTS
7
- # constants
8
- BAM_CMATCH = 0
9
- BAM_CINS = 1
10
- BAM_CDEL = 2
11
- BAM_CREF_SKIP = 3
12
- BAM_CSOFT_CLIP = 4
13
- BAM_CHARD_CLIP = 5
14
- BAM_CPAD = 6
15
- BAM_CEQUAL = 7
16
- BAM_CDIFF = 8
17
- BAM_CBACK = 9
18
-
19
- BAM_CIGAR_STR = "MIDNSHP=XB"
20
- BAM_CIGAR_SHIFT = 4
21
- BAM_CIGAR_MASK = 0xf
22
- BAM_CIGAR_TYPE = 0x3C1A7
23
-
24
- # macros
25
- class << self
26
- def bam_cigar_op(c)
27
- c & BAM_CIGAR_MASK
28
- end
29
-
30
- def bam_cigar_oplen(c)
31
- c >> BAM_CIGAR_SHIFT
32
- end
33
-
34
- def bam_cigar_opchr(c)
35
- ("#{BAM_CIGAR_STR}??????")[bam_cigar_op(c)]
36
- end
37
-
38
- def bam_cigar_gen(l, o)
39
- l << BAM_CIGAR_SHIFT | o
40
- end
41
-
42
- def bam_cigar_type(o)
43
- BAM_CIGAR_TYPE >> (o << 1) & 3
44
- end
45
- end
5
+ # Generates a new unpopulated header structure.
6
+ attach_function \
7
+ :sam_hdr_init,
8
+ [],
9
+ SamHdr.by_ref
10
+
11
+ # Read the header from a BAM compressed file.
12
+ attach_function \
13
+ :bam_hdr_read,
14
+ [BGZF],
15
+ SamHdr.by_ref
16
+
17
+ # Writes the header to a BAM file.
18
+ attach_function \
19
+ :bam_hdr_write,
20
+ [BGZF, SamHdr],
21
+ :int
22
+
23
+ # Frees the resources associated with a header.
24
+ attach_function \
25
+ :sam_hdr_destroy,
26
+ [SamHdr],
27
+ :void
28
+
29
+ # Duplicate a header structure.
30
+ attach_function \
31
+ :sam_hdr_dup,
32
+ [SamHdr],
33
+ SamHdr.by_ref
34
+
35
+ # Create a header from existing text.
36
+ attach_function \
37
+ :sam_hdr_parse,
38
+ %i[size_t string],
39
+ SamHdr.by_ref
40
+
41
+ # Read a header from a SAM, BAM or CRAM file.
42
+ attach_function \
43
+ :sam_hdr_read,
44
+ [SamFile],
45
+ SamHdr.by_ref
46
+
47
+ # Write a header to a SAM, BAM or CRAM file.
48
+ attach_function \
49
+ :sam_hdr_write,
50
+ [SamFile, SamHdr],
51
+ :int
52
+
53
+ # Returns the current length of the header text.
54
+ attach_function \
55
+ :sam_hdr_length,
56
+ [SamHdr],
57
+ :size_t
58
+
59
+ # Returns the text representation of the header.
60
+ attach_function \
61
+ :sam_hdr_str,
62
+ [SamHdr],
63
+ :string
64
+
65
+ # Returns the number of references in the header.
66
+ attach_function \
67
+ :sam_hdr_nref,
68
+ [SamHdr],
69
+ :int
70
+
71
+ # Add formatted lines to an existing header.
72
+ attach_function \
73
+ :sam_hdr_add_lines,
74
+ [SamHdr, :string, :size_t],
75
+ :int
76
+
77
+ # Adds a single line to an existing header.
78
+ attach_function \
79
+ :sam_hdr_add_line,
80
+ [SamHdr, :string, :varargs],
81
+ :int
82
+
83
+ # Returns a complete line of formatted text for a given type and ID.
84
+ attach_function \
85
+ :sam_hdr_find_line_id,
86
+ [SamHdr, :string, :string, :string, KString],
87
+ :int
88
+
89
+ # Returns a complete line of formatted text for a given type and index.
90
+ attach_function \
91
+ :sam_hdr_find_line_pos,
92
+ [SamHdr, :string, :int, KString],
93
+ :int
94
+
95
+ # Remove a line with given type / id from a header
96
+ attach_function \
97
+ :sam_hdr_remove_line_id,
98
+ [SamHdr, :string, :string, :string],
99
+ :int
100
+
101
+ # Remove nth line of a given type from a header
102
+ attach_function \
103
+ :sam_hdr_remove_line_pos,
104
+ [SamHdr, :string, :int],
105
+ :int
106
+
107
+ # Add or update tag key,value pairs in a header line.
108
+ attach_function \
109
+ :sam_hdr_update_line,
110
+ [SamHdr, :string, :string, :string, :varargs],
111
+ :int
112
+
113
+ # Remove all lines of a given type from a header, except the one matching an ID
114
+ attach_function \
115
+ :sam_hdr_remove_except,
116
+ [SamHdr, :string, :string, :string],
117
+ :int
118
+
119
+ # Remove header lines of a given type, except those in a given ID set
120
+ attach_function \
121
+ :sam_hdr_remove_lines,
122
+ [SamHdr, :string, :string, :pointer],
123
+ :int
124
+
125
+ # Count the number of lines for a given header type
126
+ attach_function \
127
+ :sam_hdr_count_lines,
128
+ [SamHdr, :string],
129
+ :int
130
+
131
+ # Index of the line for the types that have dedicated look-up tables (SQ, RG, PG)
132
+ attach_function \
133
+ :sam_hdr_line_index,
134
+ [SamHdr, :string, :string],
135
+ :int
136
+
137
+ # Id key of the line for the types that have dedicated look-up tables (SQ, RG, PG)
138
+ attach_function \
139
+ :sam_hdr_line_name,
140
+ [SamHdr, :string, :int],
141
+ :string
142
+
143
+ # Return the value associated with a key for a header line identified by ID_key:ID_val
144
+ attach_function \
145
+ :sam_hdr_find_tag_id,
146
+ [SamHdr, :string, :string, :string, :string, KString],
147
+ :int
148
+
149
+ # Return the value associated with a key for a header line identified by position
150
+ attach_function \
151
+ :sam_hdr_find_tag_pos,
152
+ [SamHdr, :string, :int, :string, KString],
153
+ :int
154
+
155
+ # Remove the key from the line identified by type, ID_key and ID_value.
156
+ attach_function \
157
+ :sam_hdr_remove_tag_id,
158
+ [SamHdr, :string, :string, :string, :string],
159
+ :int
160
+
161
+ # Get the target id for a given reference sequence name
162
+ attach_function \
163
+ :sam_hdr_name2tid,
164
+ [SamHdr, :string],
165
+ :int
166
+
167
+ # Get the reference sequence name from a target index
168
+ attach_function \
169
+ :sam_hdr_tid2name,
170
+ [SamHdr, :int],
171
+ :string
172
+
173
+ # Get the reference sequence length from a target index
174
+ attach_function \
175
+ :sam_hdr_tid2len,
176
+ [SamHdr, :int],
177
+ :hts_pos_t
178
+
179
+ # Generate a unique \@PG ID: value
180
+ attach_function \
181
+ :sam_hdr_pg_id,
182
+ [SamHdr, :string],
183
+ :string
184
+
185
+ # Add an \@PG line.
186
+ attach_function \
187
+ :sam_hdr_add_pg,
188
+ [SamHdr, :string, :varargs],
189
+ :int
190
+
191
+ # A function to help with construction of CL tags in @PG records.
192
+ attach_function \
193
+ :stringify_argv,
194
+ %i[int pointer],
195
+ :string
196
+
197
+ # Increments the reference count on a header
198
+ attach_function \
199
+ :sam_hdr_incr_ref,
200
+ [SamHdr],
201
+ :void
202
+
203
+ # Create a new bam1_t alignment structure
204
+ attach_function \
205
+ :bam_init1,
206
+ [],
207
+ Bam1.by_ref
208
+
209
+ # Destroy a bam1_t structure
210
+ attach_function \
211
+ :bam_destroy1,
212
+ [Bam1],
213
+ :void
214
+
215
+ # Read a BAM format alignment record
216
+ attach_function \
217
+ :bam_read1,
218
+ [BGZF, Bam1],
219
+ :int
220
+
221
+ # Write a BAM format alignment record
222
+ attach_function \
223
+ :bam_write1,
224
+ [BGZF, Bam1],
225
+ :int
226
+
227
+ # Copy alignment record data
228
+ attach_function \
229
+ :bam_copy1,
230
+ [Bam1, Bam1],
231
+ Bam1.by_ref
232
+
233
+ # Create a duplicate alignment record
234
+ attach_function \
235
+ :bam_dup1,
236
+ [Bam1],
237
+ Bam1.by_ref
238
+
239
+ # Set all components of an alignment structure
240
+ attach_function \
241
+ :bam_set1,
242
+ [Bam1,
243
+ :size_t,
244
+ :string,
245
+ :uint16_t,
246
+ :int32_t,
247
+ :hts_pos_t,
248
+ :uint8_t,
249
+ :size_t,
250
+ :string,
251
+ :int32_t,
252
+ :hts_pos_t,
253
+ :hts_pos_t,
254
+ :size_t,
255
+ :string,
256
+ :string,
257
+ :size_t],
258
+ :int
259
+
260
+ # Calculate query length from CIGAR data
261
+ attach_function \
262
+ :bam_cigar2qlen,
263
+ %i[int pointer],
264
+ :int64
265
+
266
+ # Calculate reference length from CIGAR data
267
+ attach_function \
268
+ :bam_cigar2rlen,
269
+ %i[int pointer],
270
+ :hts_pos_t
271
+
272
+ # Calculate the rightmost base position of an alignment on the reference genome.
273
+ attach_function \
274
+ :bam_endpos,
275
+ [Bam1],
276
+ :hts_pos_t
277
+
278
+ attach_function \
279
+ :bam_str2flag,
280
+ [:string],
281
+ :int
282
+
283
+ attach_function \
284
+ :bam_flag2str,
285
+ [:int],
286
+ :string
287
+
288
+ # Set the name of the query
289
+ attach_function \
290
+ :bam_set_qname,
291
+ [Bam1, :string],
292
+ :int
293
+
294
+ # Parse a CIGAR string into a uint32_t array
295
+ attach_function \
296
+ :sam_parse_cigar,
297
+ %i[string pointer pointer pointer],
298
+ :ssize_t
299
+
300
+ # Parse a CIGAR string into a bam1_t struct
301
+ attach_function \
302
+ :bam_parse_cigar,
303
+ [:string, :pointer, Bam1],
304
+ :ssize_t
305
+
306
+ # Initialise fp->idx for the current format type for SAM, BAM and CRAM types .
307
+ attach_function \
308
+ :sam_idx_init,
309
+ [HtsFile, SamHdr, :int, :string],
310
+ :int
311
+
312
+ # Writes the index initialised with sam_idx_init to disk.
313
+ attach_function \
314
+ :sam_idx_save,
315
+ [HtsFile],
316
+ :int
317
+
318
+ # Load a BAM (.csi or .bai) or CRAM (.crai) index file
319
+ attach_function \
320
+ :sam_index_load,
321
+ [HtsFile, :string],
322
+ HtsIdx.by_ref
323
+
324
+ # Load a specific BAM (.csi or .bai) or CRAM (.crai) index file
325
+ attach_function \
326
+ :sam_index_load2,
327
+ [HtsFile, :string, :string],
328
+ HtsIdx.by_ref
329
+
330
+ # Load or stream a BAM (.csi or .bai) or CRAM (.crai) index file
331
+ attach_function \
332
+ :sam_index_load3,
333
+ [HtsFile, :string, :string, :int],
334
+ HtsIdx.by_ref
335
+
336
+ # Generate and save an index file
337
+ attach_function \
338
+ :sam_index_build,
339
+ %i[string int],
340
+ :int
341
+
342
+ # Generate and save an index to a specific file
343
+ attach_function \
344
+ :sam_index_build2,
345
+ %i[string string int],
346
+ :int
347
+
348
+ # Generate and save an index to a specific file
349
+ attach_function \
350
+ :sam_index_build3,
351
+ %i[string string int int],
352
+ :int
353
+
354
+ # Create a BAM/CRAM iterator
355
+ attach_function \
356
+ :sam_itr_queryi,
357
+ [HtsIdx, :int, :hts_pos_t, :hts_pos_t],
358
+ HtsItr.by_ref
359
+
360
+ # Create a SAM/BAM/CRAM iterator
361
+ attach_function \
362
+ :sam_itr_querys,
363
+ [HtsIdx, SamHdr, :string],
364
+ HtsItr.by_ref
365
+
366
+ # Create a multi-region iterator
367
+ attach_function \
368
+ :sam_itr_regions,
369
+ [HtsIdx, SamHdr, HtsReglist, :uint],
370
+ HtsItr.by_ref
46
371
 
47
- BAM_FPAIRED = 1
48
- BAM_FPROPER_PAIR = 2
49
- BAM_FUNMAP = 4
50
- BAM_FMUNMAP = 8
51
- BAM_FREVERSE = 16
52
- BAM_FMREVERSE = 32
53
- BAM_FREAD1 = 64
54
- BAM_FREAD2 = 128
55
- BAM_FSECONDARY = 256
56
- BAM_FQCFAIL = 512
57
- BAM_FDUP = 1024
58
- BAM_FSUPPLEMENTARY = 2048
59
-
60
- # macros
61
- # function-like macros
62
- class << self
63
- def bam_is_rev(b)
64
- b[:core][:flag] & BAM_FREVERSE != 0
65
- end
66
-
67
- def bam_is_mrev(b)
68
- b[:core][:flag] & BAM_FMREVERSE != 0
69
- end
70
-
71
- def bam_get_qname(b)
72
- b[:data]
73
- end
74
-
75
- def bam_get_cigar(b)
76
- b[:data] + b[:core][:l_qname]
77
- end
78
-
79
- def bam_get_seq(b)
80
- b[:data] + (b[:core][:n_cigar] << 2) + b[:core][:l_qname]
81
- end
82
-
83
- def bam_get_qual(b)
84
- b[:data] + (b[:core][:n_cigar] << 2) + b[:core][:l_qname] + ((b[:core][:l_qseq] + 1) >> 1)
85
- end
86
-
87
- def bam_get_aux(b)
88
- b[:data] + (b[:core][:n_cigar] << 2) + b[:core][:l_qname] + ((b[:core][:l_qseq] + 1) >> 1) + b[:core][:l_qseq]
89
- end
90
-
91
- def bam_get_l_aux(b)
92
- b[:l_data] - (b[:core][:n_cigar] << 2) - b[:core][:l_qname] - b[:core][:l_qseq] - ((b[:core][:l_qseq] + 1) >> 1)
93
- end
94
-
95
- def bam_seqi(s, i)
96
- s[(i) >> 1].read_uint8 >> ((~i & 1) << 2) & 0xf
97
- end
98
-
99
- # def bam_set_seqi(s, i, b)
372
+ # Create a multi-region iterator
373
+ attach_function \
374
+ :sam_itr_regarray,
375
+ [HtsIdx, SamHdr, :pointer, :uint],
376
+ HtsItr.by_ref
377
+
378
+ # Get the next read from a SAM/BAM/CRAM iterator
379
+ def self.sam_itr_next(htsfp, itr, r)
380
+ # FIXME: check if htsfp is compressed BGZF
381
+ raise("Null iterator") if itr.null?
382
+
383
+ # FIXME: check multi
384
+ hts_itr_next(htsfp[:fp][:bgzf], itr, r, htsfp)
100
385
  end
386
+
387
+ attach_function \
388
+ :sam_parse_region,
389
+ [SamHdr, :string, :pointer, :pointer, :pointer, :int],
390
+ :string
391
+
392
+ # SAM I/O
393
+
394
+ # macros (or alias)
395
+ # sam_open
396
+ # sam_open_format
397
+ # sam_close
398
+
399
+ attach_function \
400
+ :sam_open_mode,
401
+ %i[string string string],
402
+ :int
403
+
404
+ # A version of sam_open_mode that can handle ,key=value options.
405
+ attach_function \
406
+ :sam_open_mode_opts,
407
+ %i[string string string],
408
+ :string
409
+
410
+ attach_function \
411
+ :sam_hdr_change_HD,
412
+ [SamHdr, :string, :string],
413
+ :int
414
+
415
+ attach_function \
416
+ :sam_parse1,
417
+ [KString, SamHdr, Bam1],
418
+ :int
419
+
420
+ attach_function \
421
+ :sam_format1,
422
+ [SamHdr, Bam1, KString],
423
+ :int
424
+
425
+ # Read a record from a file
426
+ attach_function \
427
+ :sam_read1,
428
+ [HtsFile, SamHdr, Bam1],
429
+ :int
430
+
431
+ # Write a record to a file
432
+ attach_function \
433
+ :sam_write1,
434
+ [HtsFile, SamHdr, Bam1],
435
+ :int
436
+
437
+ # Checks whether a record passes an hts_filter.
438
+ attach_function \
439
+ :sam_passes_filter,
440
+ [SamHdr, Bam1, :pointer], # hts_filter_t
441
+ :int
442
+
443
+ # Return a pointer to an aux record
444
+ attach_function \
445
+ :bam_aux_get,
446
+ [Bam1, :string], # FIXME
447
+ :pointer
448
+
449
+ # Get an integer aux value
450
+ attach_function \
451
+ :bam_aux2i,
452
+ [:pointer],
453
+ :int64
454
+
455
+ # Get an integer aux value
456
+ attach_function \
457
+ :bam_aux2f,
458
+ [:pointer],
459
+ :double
460
+
461
+ # Get a character aux value
462
+ attach_function \
463
+ :bam_aux2A,
464
+ [:pointer],
465
+ :char
466
+
467
+ # Get a string aux value
468
+ attach_function \
469
+ :bam_aux2Z,
470
+ [:pointer],
471
+ :string
472
+
473
+ # Get the length of an array-type ('B') tag
474
+ attach_function \
475
+ :bam_auxB_len,
476
+ [:pointer],
477
+ :uint
478
+
479
+ # Get an integer value from an array-type tag
480
+ attach_function \
481
+ :bam_auxB2i,
482
+ %i[pointer uint],
483
+ :int64
484
+
485
+ # Get a floating-point value from an array-type tag
486
+ attach_function \
487
+ :bam_auxB2f,
488
+ %i[pointer uint],
489
+ :double
490
+
491
+ # Append tag data to a bam record
492
+ attach_function \
493
+ :bam_aux_append,
494
+ [Bam1, :string, :string, :int, :pointer],
495
+ :int
496
+
497
+ # Delete tag data from a bam record
498
+ attach_function \
499
+ :bam_aux_del,
500
+ [Bam1, :pointer],
501
+ :int
502
+
503
+ # Update or add a string-type tag
504
+ attach_function \
505
+ :bam_aux_update_str,
506
+ [Bam1, :string, :int, :string],
507
+ :int
508
+
509
+ # Update or add an integer tag
510
+ attach_function \
511
+ :bam_aux_update_int,
512
+ [Bam1, :string, :int64],
513
+ :int
514
+
515
+ # Update or add a floating-point tag
516
+ attach_function \
517
+ :bam_aux_update_float,
518
+ [Bam1, :string, :float],
519
+ :int
520
+
521
+ # Update or add an array tag
522
+ attach_function \
523
+ :bam_aux_update_array,
524
+ [Bam1, :string, :uint8, :uint32, :pointer],
525
+ :int
526
+
527
+ # sets an iterator over multiple
528
+ attach_function \
529
+ :bam_plp_init,
530
+ %i[bam_plp_auto_f pointer],
531
+ :bam_plp
532
+
533
+ attach_function \
534
+ :bam_plp_destroy,
535
+ [:bam_plp],
536
+ :void
537
+
538
+ attach_function \
539
+ :bam_plp_push,
540
+ [:bam_plp, Bam1],
541
+ :int
542
+
543
+ attach_function \
544
+ :bam_plp_next,
545
+ %i[bam_plp pointer pointer pointer],
546
+ BamPileup1.by_ref
547
+
548
+ attach_function \
549
+ :bam_plp_auto,
550
+ %i[bam_plp pointer pointer pointer],
551
+ BamPileup1.by_ref
552
+
553
+ attach_function \
554
+ :bam_plp64_next,
555
+ %i[bam_plp pointer pointer pointer],
556
+ BamPileup1.by_ref
557
+
558
+ attach_function \
559
+ :bam_plp64_auto,
560
+ %i[bam_plp pointer pointer pointer],
561
+ BamPileup1.by_ref
562
+
563
+ attach_function \
564
+ :bam_plp_set_maxcnt,
565
+ %i[bam_plp int],
566
+ :void
567
+
568
+ attach_function \
569
+ :bam_plp_reset,
570
+ [:bam_plp],
571
+ :void
572
+
573
+ callback :bam_plp_callback_funcion, [:pointer, Bam1, BamPileupCd], :int
574
+
575
+ # sets a callback to initialise any per-pileup1_t fields.
576
+ attach_function \
577
+ :bam_plp_constructor,
578
+ %i[bam_plp bam_plp_callback_funcion],
579
+ :void
580
+
581
+ attach_function \
582
+ :bam_plp_destructor,
583
+ %i[bam_plp bam_plp_callback_funcion],
584
+ :void
585
+
586
+ # Get pileup padded insertion sequence
587
+ attach_function \
588
+ :bam_plp_insertion,
589
+ [BamPileup1, KString, :pointer],
590
+ :int
591
+
592
+ # Get pileup padded insertion sequence, including base modifications
593
+ attach_function \
594
+ :bam_plp_insertion_mod,
595
+ [BamPileup1, :pointer, KString, :pointer],
596
+ :int
597
+
598
+ attach_function \
599
+ :bam_mplp_init,
600
+ %i[int bam_plp_auto_f pointer],
601
+ :bam_mplp
602
+
603
+ attach_function \
604
+ :bam_mplp_init_overlaps,
605
+ [:bam_mplp],
606
+ :int
607
+
608
+ attach_function \
609
+ :bam_mplp_destroy,
610
+ [:bam_mplp],
611
+ :void
612
+
613
+ attach_function \
614
+ :bam_mplp_set_maxcnt,
615
+ %i[bam_mplp int],
616
+ :void
617
+
618
+ attach_function \
619
+ :bam_mplp_auto,
620
+ %i[bam_mplp pointer pointer pointer pointer], # BamPileup1T
621
+ :int
622
+
623
+ attach_function \
624
+ :bam_mplp64_auto,
625
+ %i[bam_mplp pointer pointer pointer pointer], # BamPileup1T
626
+ :int
627
+
628
+ attach_function \
629
+ :bam_mplp_reset,
630
+ [:bam_mplp],
631
+ :void
632
+
633
+ attach_function \
634
+ :bam_mplp_constructor,
635
+ %i[bam_mplp bam_plp_callback_funcion],
636
+ :void
637
+
638
+ attach_function \
639
+ :bam_mplp_destructor,
640
+ %i[bam_mplp bam_plp_callback_funcion],
641
+ :void
642
+
643
+ attach_function \
644
+ :sam_cap_mapq,
645
+ [Bam1, :string, :hts_pos_t, :int],
646
+ :int
647
+
648
+ attach_function \
649
+ :sam_prob_realn,
650
+ [Bam1, :string, :hts_pos_t, :int],
651
+ :int
652
+
653
+ # Allocates an hts_base_mode_state.
654
+ attach_function \
655
+ :hts_base_mod_state_alloc,
656
+ [],
657
+ :pointer # hts_base_mod_state
658
+
659
+ # Destroys an hts_base_mode_state.
660
+ attach_function \
661
+ :hts_base_mod_state_free,
662
+ [:pointer], # hts_base_mod_state
663
+ :void
664
+
665
+ # Parses the Mm and Ml tags out of a bam record.
666
+ attach_function \
667
+ :bam_parse_basemod,
668
+ [Bam1, :pointer],
669
+ :int
670
+
671
+ # Returns modification status for the next base position in the query seq.
672
+ attach_function \
673
+ :bam_mods_at_next_pos,
674
+ [Bam1, :pointer, :pointer, :int],
675
+ :int
676
+
677
+ # Finds the next location containing base modifications and returns them
678
+ attach_function \
679
+ :bam_next_basemod,
680
+ [Bam1, :pointer, :pointer, :int, :pointer],
681
+ :int
682
+
683
+ # Returns modification status for a specific query position.
684
+ attach_function \
685
+ :bam_mods_at_qpos,
686
+ [Bam1, :int, :pointer, :pointer, :int],
687
+ :int
101
688
  end
102
689
  end
690
+
691
+ require_relative "sam_funcs"