-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgeno.c
More file actions
311 lines (268 loc) · 10.4 KB
/
Copy pathgeno.c
File metadata and controls
311 lines (268 loc) · 10.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
#include <assert.h>
#include <fcntl.h>
#include <math.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include "geno.h"
// each entry is 2 bits
size_t padded_bytes_needed(size_t n){
return (size_t) ceil(n/4.0);
}
void genotype_matrix_fail( const char * message){
fprintf(stderr, "genotype matrix failure: %s\n", message);
exit(-1);
}
void genotype_matrix_initialize(genotype_matrix *this){
this->num_rows = 0;
this->num_columns = 0;
this->row_size_bytes = 0;
this->individual_file_hash = 0;
this->snp_file_hash = 0;
this->memory_map_start = NULL;
this->memory_map_length = 0;
this->genotypes = NULL;
this->transpose = false;
this->get_position = 0;
}
void genotype_matrix_setup_dimensions(genotype_matrix *this, size_t num_samples, size_t num_snps, bool transpose){
this->transpose = transpose;
if(this->transpose){
this->num_rows = num_samples;
this->num_columns = num_snps;
this->get_position = transpose_position;
this->row_size_bytes = padded_bytes_needed(this->num_columns);
}
else{
this->num_rows = num_snps;
this->num_columns = num_samples;
this->get_position = normal_position;
size_t needed_bytes = padded_bytes_needed(this->num_columns);
this->row_size_bytes = (needed_bytes > GENO_HEADER_SIZE) ? needed_bytes : GENO_HEADER_SIZE;
}
}
void genotype_matrix_allocate(genotype_matrix *this, size_t num_samples, size_t num_snps, bool transpose){
genotype_matrix_setup_dimensions(this, num_samples, num_snps, transpose);
size_t size = genotype_matrix_byte_size(this);
this->genotypes = (unsigned char *) malloc(size);
// missing data is all 1's
memset(this->genotypes, 0xFF, size);
// Nick writes unused end-of-row padding to 0. We replicate that here.
size_t max = this->row_size_bytes * 4;
for (size_t row = 0; row < this->num_rows; row++){
unsigned char *row_start = this->genotypes + (row * this->row_size_bytes);
assert(max - this->num_columns < 4);
for (size_t offset = this->num_columns; offset < max; offset++){
array_set_genotype(row_start, offset, 0);
}
}
}
void genotype_matrix_destructor(genotype_matrix *this){
if(this->memory_map_start){
munmap(this->memory_map_start, this->memory_map_length);
}
else if(this->genotypes != NULL){
free(this->genotypes);
}
genotype_matrix_initialize(this);
}
size_t genotype_matrix_byte_size(const genotype_matrix *this){
return this->num_rows * this->row_size_bytes * sizeof(unsigned char);
}
int genotype_matrix_read_file(genotype_matrix *this, const char * const genotype_filename, int flags, const unsigned int *individual_file_hash, const unsigned int *snp_file_hash){
genotype_matrix_initialize(this);
struct stat sb;
size_t num_samples, num_snps;
int fd = open(genotype_filename, O_RDONLY);
if(fd < 0){
return -1;
}
char header[GENO_HEADER_SIZE+1];
header[GENO_HEADER_SIZE] = 0;
char matrix_type[GENO_HEADER_SIZE+1];
fstat(fd, &sb);
this->memory_map_length = sb.st_size;
this->memory_map_start = mmap(NULL, this->memory_map_length, PROT_READ, MAP_PRIVATE | flags, fd, 0);
if(this->memory_map_start == MAP_FAILED){
return -2;
}
close(fd);
// read header
strncpy(header, this->memory_map_start, GENO_HEADER_SIZE);
//printf("%s\n", header);
//printf("%d\n", strlen(header));
sscanf(header, "%s %zd %zd %x %x", matrix_type, &num_samples, &num_snps, &(this->individual_file_hash), &(this->snp_file_hash));
printf("matrix type is %s\n", matrix_type);
//printf("samples %zd\n", num_samples);
//printf("snps %zd\n", num_snps);
bool transpose;
if(strncmp(matrix_type, "GENO", 4) == 0){
transpose = false;
} else if (strncmp(matrix_type, "TGENO", 5) == 0){
transpose = true;
} else{
fprintf(stderr, "Unhandled file type");
return -3;
}
genotype_matrix_setup_dimensions(this, num_samples, num_snps, transpose);
// set genotype pointer
size_t header_size = GENO_HEADER_SIZE;
if(!this->transpose){
header_size = this->row_size_bytes;
}
this->genotypes = (unsigned char *) this->memory_map_start + header_size;
// verify size
size_t expected_size = header_size + genotype_matrix_byte_size(this);
if (this->memory_map_length != expected_size){
fprintf(stderr, "Unexpected %s size. Expected %ld was %ld\n", matrix_type, expected_size, this->memory_map_length);
return -4;
}
// verify hashes
if(individual_file_hash && (*individual_file_hash != this->individual_file_hash)){
fprintf(stderr, "individual file hash mismatch %x %x", *individual_file_hash, this->individual_file_hash);
return -5;
}
if(snp_file_hash && (*snp_file_hash != this->snp_file_hash)){
fprintf(stderr, "snp file hash mismatch %x %x", *snp_file_hash, this->snp_file_hash);
return -6;
}
return 0;
}
int genotype_matrix_read_file_full(genotype_matrix *this, const char * const genotype_filename, int flags, const unsigned int *individual_file_hash, const unsigned int *snp_file_hash){
int result = genotype_matrix_read_file(this, genotype_filename, flags, individual_file_hash, snp_file_hash);
if (result < 0){
return result;
}
size_t genotype_size = genotype_matrix_byte_size(this);
unsigned char *in_memory = malloc(genotype_size);
memcpy(in_memory, this->genotypes, genotype_size);
munmap(this->memory_map_start, this->memory_map_length);
this->memory_map_start = NULL;
this->genotypes = in_memory;
return 0;
}
void genotype_matrix_header(const genotype_matrix *this, bool transpose, char *dest){
char *matrix_type = transpose ? "TGENO" : "GENO";
size_t num_samples = this->transpose ? this->num_rows : this->num_columns;
size_t num_snps = this->transpose ? this->num_columns : this->num_rows;
sprintf(dest, "%s %7ld %7ld %x %x", matrix_type, num_samples, num_snps, this->individual_file_hash, this->snp_file_hash);
}
int genotype_matrix_write_packed_file(const genotype_matrix *this, const char *filename, bool transpose){
FILE *output = fopen(filename, "w");
// first write out header
int header_size = GENO_HEADER_SIZE;
if(!transpose){ // header uses row size in packed ancestry map format
size_t output_row_size = padded_bytes_needed(genotype_matrix_num_samples(this));
if (header_size < output_row_size){
header_size = output_row_size;
}
}
//printf("header_size %d\n", header_size);
char *buffer = malloc(header_size);
memset(buffer, 0, header_size);
genotype_matrix_header(this, transpose, buffer);
size_t bytes_written = fwrite(buffer, sizeof(char), header_size, output);
if(bytes_written != header_size){
genotype_matrix_fail("header write failure");
}
free(buffer);
buffer = NULL;
// if matrix is in same orientation, we simply write it out
if(this->transpose == transpose){
bytes_written = fwrite(this->genotypes, sizeof(unsigned char), genotype_matrix_byte_size(this), output);
if (bytes_written != genotype_matrix_byte_size(this)){
genotype_matrix_fail("write failure");
}
}
else{ // need to switch rows and columns for output
size_t output_row_size = padded_bytes_needed(this->num_rows);
if(!transpose && output_row_size < GENO_HEADER_SIZE){
output_row_size = GENO_HEADER_SIZE;
}
//printf("output row size %d\n", output_row_size);
buffer = malloc(output_row_size);
for (size_t column=0; column < this->num_columns; column++){
//memset(buffer, 0xFF, output_row_size);
memset(buffer, 0x00, output_row_size);
for(size_t row = 0; row < this->num_rows; row++){
bit_array_position read_position;
row_position(column, &read_position);
read_position.byte_index += row * this->row_size_bytes;
unsigned char genotype = genotype_matrix_get_genotype_internal(this, &read_position);
array_set_genotype((unsigned char *) buffer, row, genotype);
}
bytes_written = fwrite(buffer, sizeof(unsigned char), output_row_size, output);
if(bytes_written != output_row_size){
genotype_matrix_fail("write failure");
}
}
free(buffer);
}
fflush(output);
fclose(output);
return 0;
}
void row_position(size_t offset, bit_array_position *position){
position->byte_index = offset / 4;
position->bit_shift = 6 - (offset % 4) * 2;
}
// one row is one SNP, all samples
void normal_position(const genotype_matrix *this, size_t sample, size_t snp, bit_array_position *position){
row_position(sample, position);
position->byte_index += (this->row_size_bytes * snp);
}
// one row is one sample, all SNPs
void transpose_position(const genotype_matrix *this, size_t sample, size_t snp, bit_array_position *position) {
row_position(snp, position);
position->byte_index += (this->row_size_bytes * sample);
}
unsigned char genotype_matrix_get_genotype_internal(const genotype_matrix *this, const bit_array_position *position){
unsigned char genotype_byte = this->genotypes[position->byte_index];
return (genotype_byte >> position->bit_shift) & 0x3;
}
unsigned int genotype_matrix_get_snp_hash(const genotype_matrix *this){
return this->snp_file_hash;
}
void genotype_matrix_set_snp_hash(genotype_matrix *this, unsigned int hash){
this->snp_file_hash = hash;
}
unsigned int genotype_matrix_get_individual_hash(const genotype_matrix *this){
return this->individual_file_hash;
}
void genotype_matrix_set_individual_hash(genotype_matrix *this, unsigned int hash){
this->individual_file_hash = hash;
}
size_t genotype_matrix_num_snps(const genotype_matrix *this){
return (this->transpose) ? this->num_columns : this->num_rows;
}
size_t genotype_matrix_num_samples(const genotype_matrix *this){
return (this->transpose) ? this->num_rows : this->num_columns;
}
unsigned char genotype_matrix_get_genotype(const genotype_matrix *this, size_t sample, size_t snp){
bit_array_position position;
this->get_position(this, sample, snp, &position);
return genotype_matrix_get_genotype_internal(this, &position);
}
void array_set_genotype_internal(unsigned char *genotypes, const bit_array_position *position, char value){
if(value < 0){
value = 0x3;
}
unsigned char genotype_byte = genotypes[position->byte_index];
unsigned char value_mask = 0x3;
unsigned char existing_mask = 0xFF ^ (value_mask << position->bit_shift);
genotypes[position->byte_index] = (genotype_byte & existing_mask) | ( (value & value_mask) << position->bit_shift);
}
void array_set_genotype(unsigned char *genotypes, size_t offset, char value){
bit_array_position position;
row_position(offset, &position);
array_set_genotype_internal(genotypes, &position, value);
}
void genotype_matrix_set_genotype(genotype_matrix *this, size_t sample, size_t snp, char value){
bit_array_position position;
this->get_position(this, sample, snp, &position);
array_set_genotype_internal(this->genotypes, &position, value);
}