-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathk2text.c
More file actions
1371 lines (1266 loc) · 56.9 KB
/
Copy pathk2text.c
File metadata and controls
1371 lines (1266 loc) · 56.9 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
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* Routines for converting boolean matrices in sparse text form
(ie one entry per line) to/from compressed k^2 tree representation
Note: internally matrix dimensions are always of the form 2^k times the size
of a minimatrix (those stored at the leaves of the tree), with k>0
(somewhere this is called the k2_internal_size); the input can be of
any size (not larger than that) and the k2 matrix is padded with 0's
(virtually since they are not stored)
The conversion txt->k2 is done using an auxiliary "interleaved" array:
each matrix entry consists of two uint32_t (row and column indices).
A unique entry identifier is obtained interleaving the bits of the two indices:
as in: r31 c31 ... r2 c2 r1 c1 r0 c0 where
ri is the i-th bit of the row index and ci is the i-th bit of the column index
When such interleaved values are numerically sorted the entries appear in
exactly the same order such entries are visited in a predorder visit of the k2 tree
Hence submatrices can be represented by subintervals of the interleaved array
Note that the size of the interleaved array is equal to the number of
nonzeros, which we assume is less than 2^64, hence indices in the
array can be stored in a size_t. However, the single entry store the
row and column index so it must be able to store a number of bits equal
to (2 x bits in a single index).
Currently the maximum allowed size is 2^32, so each index takes 32 bits
and the interleaved array can be of int64_t's. To support larger
matrices, say up to 2^40, the entries of the interleaved array ia[]
and the related variables (imin,left,mid,right) must be enlarged.
This can be done using uint128_t for the scalars and an appropriate
byte array for ia[].
Recall than when working with values >= 2^32 stored in an uint64_t
we cannot safely compute products: this is why we have the functions
a_lt_b2 and a_eq_b2 testing whether a<b*b or a==b*b without multiplications
The conversion k2->txt is done doing a visit of the tree in preorder and
each time a nonzero entry is found its indices are written to the output file
Currently only the minimatrix sizes 2 and 4 are supported
Copyright August 2023-today --- giovanni.manzini@unipi.it
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <inttypes.h>
#include "minimats.c" // includes k2.h bbm.h
#include "libsais/include/libsais64.h"
#include "pointers.h"
#include "rank_0000.h"
// prototypes of static functions
static uint64_t *create_ia(FILE *f, size_t *n, size_t *msize, size_t xsize);
static size_t mread_from_ia(uint64_t ia[], size_t n, size_t msize, k2mat_t *a);
static void mencode_ia(uint64_t *ia, size_t n, uint64_t imin, size_t size, k2mat_t *c);
static void mdecode_to_textfile(FILE *outfile, size_t msize, size_t i, size_t j, size_t size, const k2mat_t *c, size_t *pos);
static size_t binsearch(uint64_t *ia, size_t n, uint64_t x);
static inline bool a_eq_b2(uint64_t a, uint64_t b);
static inline bool a_lt_b2(uint64_t a, uint64_t b);
// read a matrix from the text file :iname (one entry per line)
// and store it in k2 format
// the compressed matrix is stored to :a and its size to a->realsize
// if :xsize>0 that value is forced to be the size of k2 matrix
// return the actual size of the k2 matrix
// since entries are encoded in 64 bits, each index can be at most 32 bits
// so the maximum matrix size is 2^32 (change ia[],imin,imax type to go further)
size_t mread_from_textfile(k2mat_t *a, char *iname, size_t xsize)
{
assert(iname!=NULL && a!=NULL);
FILE *f = fopen(iname,"rt");
if(f==NULL) quit("mread_from_textfile: cannot open input file",__LINE__,__FILE__);
// generate interleaved array from input file
size_t n; // number of entries
size_t msize; // computed matrix size;
// since we are storing entries in 64 bits each index must fit in 32 bits
if(xsize>1ULL+UINT32_MAX) quit("mread_from_textfile: matrix too large, current limit is 2^32",__LINE__,__FILE__);
uint64_t *ia = create_ia(f,&n,&msize,xsize);
assert(xsize==0 || msize==xsize);
fclose(f);
// compress the matrix represented by the ia[] array into a k2mat_t structure
size_t asize = mread_from_ia(ia,n,msize,a);
free(ia);
a->realsize = msize; a->fullsize = asize;
return msize; // return the real size of the k2_matrix
}
// write the content of the :msize x :msize k2 matrix :a to a
// text file in one entry per line format
// respcet main_diag_1 and backp pointers. subtinfo is not used
void mwrite_to_textfile(const k2mat_t *a, char *outname)
{
assert(outname!=NULL && a!=NULL);
size_t msize = a->realsize, asize = a->fullsize;
assert(asize>=msize);
FILE *f = fopen(outname,"wt");
if(f==NULL) quit("mwrite_to_file: cannot open output file",__LINE__,__FILE__);
if(k2is_empty(a)) { // an empty k2 matrix has no entries
if(a->main_diag_1) { // but if main_diag_1 is on we have the diagonal entries
for(size_t i=0;i<msize;i++) {
int e = fprintf(f,"%zu %zu\n",i,i);
if(e<0) quit("mwrite_to_textfile: error writing to output file",__LINE__,__FILE__);
}
}
fclose(f);
return;
}
size_t pos = 0;
mdecode_to_textfile(f,msize,0,0,asize,a,&pos);
fclose(f);
assert(pos==k2pos(a)); // check we read all the k2mat_t structure
}
// subtree size computation and verification
// The next function does a dfs visit of the k2 matrix :m and writes
// the subtree sizes and the enconding of the sizes in the growing array z
// The returned value is the total size of the k2 submatrix (in nibbles)
// and in the upper 24 bits the cost of subtree encoding (ie the cost
// of encoding recursively the submatrices)
//
// Explanation:
// Assuming the root R of T has three children the subtree representation is something like:
// R111111111222222333333333333
// we need to compute and return the total size of the representation ie the length of
// the above string (ie #R(==1) + #1 + #2 + #3) and we need to store to z an encoding
// of #1 and #2 followed by the same information for the subtrees 1, 2 and 3
// This information stored in z is called the subtree information for T.
// We can fill z with a DFS visit of the k2tree: when we reach a subtree T
// above we leave two empty slots in z, call the function recursively getting
// #1, #2 and #3, store #1, #2 in the empty slots and return 1 + #1 + #2 + #3
// However, since z is used to skip subtree 1 and/or 2, in z together with
// #1 we also need to store the total information stored in z for the subtree
// rooted at 1, and the same for the subtree rooted at 2 (because we need
// to be able to skip this information as well).
// Hence, array z will not simply contain the encoding of
// <#1> <#2> info_Sub(1) info_Sub(2) info_Sub(3)
// (where < > denotes an encoding of a size, for example 7x8 byte encoding), but
// <#1> <|info_Sub(1)|> <#2> <|info_Sub(2)|> info_Sub(1) info_Sub(2) info_Sub(3)
// (to complicate things info Sub(i) is different from above because
// now includes the additional values <|info Sub()|> for the subtrees of i
// To do the computation for each subtree T this function returns 2 values:
// 1. the total length of T encoding (as before, 1+ #1 + #2 + #3)
// this is the amount of info that we need to skip in the k2 matrix to skip T
// 2. the total length of the above complete encoding of T subtree information
// this is the amont of info we need to skip in z to skip T
// If E1, E2, E3 are the lenghts of the encodings
// for the subtree Ei = |info Sub(i)| (obtained by the recursive calls)
// in z for T we store
// <#1> <E1> <#2> <E2> info_Sub(1) info_Sub(2) info_Sub(3)
// hence the cost of the encoding (of the subtrees) of T is
// |<#1>| + |<E1>| + |<#2>| + |<E2>| + E1 + E2 + E3
// note that in the above example in z's empty slots the function has
// to store the values #1 E1, #2 E2
// To simplify the code (!!!) the function returns a single uint64, with
// the less significant 40 bits storing the total length of T (item 1)
// and in the more significant 24 bits the lengths of the encodings
// Hence the recursive calls return: (here / means justapoxition lowbits/hibits)
// A1 = #1/E1, A2 = #2/E2, A3 = #3/E3
// and the function should return
// 1+ #1 + #2 + #3 / |<#1>| + |<E1>| + |<#2>| + |<|E2|>| + E1 + E2 + E3
// assuming there are no overflows, the desired value is
// 1+A1+A2+A3 + (|<#1>| + |<E1>| + |<#2>| + |<|E2|>|)<<40
// The above scheme is valid for ordinary internal nodes.
// If T is an ALL_ONES leaf, then its size is 1 and the size of the
// subtree sizes encoding is 0.
// If T has height 1, then its size is 1+#child*Minimat_node_ratio
// but there is no need to store the subtree size information since
// each subtree has size Minimat_node_ratio
// If T has depth2go==1 we need to store the subtree size information
// for T as usual, but we know that we are not storing information for
// T subtrees so E1=E2=E3=0. Since this is something we can check
// during the visit we can simply avoid storing <E1> and <E2>
// (at the moment we do store them, because it allows us
// to use more complex schemes, see below "An alternative...")
// If T has depth2go<=0 we need to report T size as usual, but
// we do not need to store any subtree information and we report 0
// as the total lenght of the subtree information
//
// An alternative scheme is to store the subtree info only for
// those subtrees which are larger than a certain threshold;
// in this case we need a strategy to recognize, when we use the matrix, if
// the subtree info is present without additional external information.
// The idea is the following (see k2split_k2() for an example):
// if m->subtinfo==NULL then there is no subtree info for the current
// tree (and its subtrees!)
// if m->subtinfo!=NULL then it contains the size of its subtrees,
// in the above example #1 and #2, the size of the last subtree
// is obtained by subtraction: #3 = #T -1 (root) - #1 -#2
// for each subtree (1,2,3) we need to compute Ei to see if there
// is subtree info stored for Ei (ie if Ei!=0)
// for 1 and 2 we just look at <E1>, <E2> that are saved in subtinfo
// while we get E3 by the formula
// m->subtinfo_size = |<#1>| + |<E1>| + |<#2>| + |<E2>| + E1 + E2 + E3
// with our simple encoding it is |<#1>| + |<E1>| + |<#2>| + |<E2>| = 2
// hence:
// E3 = m->subtinfo_size - (nchildren-1) - E1 - E2
// Note that this requires that each time we create a (sub)matrix we
// maintain the correct m->subtinfo_size (which has no other uses).
// Note that in this function we are not actually encoding (ie compressing) the values
// but only computing them (we can always encode later). Such values are stored
// to z using the above 40+24 scheme, the values then have to be stored (on disk)
// using the appropriate scheme. As a first attempt we avoid the encoding
// and just use the array z as above. In that case we can measure everything
// in uint64's so we simply have that |<#1>| + |<E1>| = 1 (1 uint64_t)
//
// Note that if we are going to use a more complex encoding (say 7x8)
// we cannot easily fill the array z from left to right since the
// the size of the empty slots we create at the beginning of T's visit
// is unknow at the beginnig of the visit since it depends on the
// subtrees content. A possibile solution could be a 2-pass encoding
// (in the first pass we compute the correct sizes but we store
// them in uint64s, then we do the actual encoding using say 7x8,
// the drawback is larger working space), or we fill z in reverse
// (first the subtrees and then T, visiting the tree right to left)
// and then we write it to disk in reverse, drawback: complex code)
// Note: the above scheme can be probably improved in speed with a minimal
// space increase. Given the structure
// <#1> <|info_Sub(1)|> <#2> <|info_Sub(2)|> info_Sub(1) info_Sub(2) info_Sub(3)
// a major issue is that to reach the info_Sub() information one has to
// to skip the "<#1> <|info_Sub(1)|> <#2> <|info_Sub(2)|>" part and then
// possibly sum together |info_Sub(1)|, |info_Sub(2)| etc. So we could
// store also len(<#1> <|info_Sub(1)|> <#2> <|info_Sub(2)|>), and we could
// store |info_Sub(1)| + |info_Sub(2)| instead of |info_Sub(2)| and so on.
// Constants to store size and esizes in a single value (moved to k2.h)
// #define BITSxTSIZE 40
// #define TSIZEMASK ( (((uint64_t) 1)<<BITSxTSIZE) -1 )
// note: potential overflow if tree sizes cannot be expressed in BITSxTSIZE bits
// and if the encoding size cannot be expressed with 64-BITSxTSIZE
// setting BITSxTSIZE at least 40 make the first event unlikely,
// while the second event is possible if we keep information for many levels.
// The first event should be detected by the test on *pos immediately
// before the final return. The second event is detected using
// __builtin_add_overflow (-ftrapv or -fsanitize do not work since they are
// for signed int and they would add extra checks for all operations)
//
#define CHECK_ESIZE_OVERFLOW 1
// The next function does a dfs visit of the k2 matrix :m and writes
// the subtree sizes and the enconding of the sizes in the growing array z
// The returned value is the total size of the k2 submatrix (in nibbles)
// and in the upper 24 bits the cost of subtree encoding (ie the cost
// of encoding recursively the submatrices)
//
// Parameters:
// size: k2 size of the current submatrix (ie 2^k * MMsize)
// m,*pos the current submatrix starts at position *pos within *m
// z: dynamic vector where the subtree information will be stored
// depth2go: # levels for which we store the subtree information
// uses the simpler .sinfo format with the last child of each node excluded
// This function (with a depth limit) can be used only for the case in which
// the subtreesize info for the last child is not stored (SIMPLEBACKPOINTERS case)
#ifdef SIMPLEBACKPOINTERS
uint64_t k2dfs_sizes(size_t size, const k2mat_t *m, size_t *pos, vu64_t *z, int32_t depth2go)
{
assert(size>MMsize);
assert(size%2==0);
assert(*pos<m->pos); // implies m is non-empty
size_t pos_save = *pos; // save starting position of subtree
node_t root = k2read_node(m,*pos); (*pos)++;
assert(root<ILLEGAL_NODE);
if(root==ALL_ONES) // all 1's matrix consists of root only,
return 1; // size is 1 subtree encoding size 0
// we have a non-singleton subtree to traverse
// compute number of children
size_t nchildren = __builtin_popcountll(root);
assert(nchildren>0 && nchildren<=4);
size_t zn_save = z->n; // save starting position in size_array[]
if(depth2go>0)
vu64_grow(z,nchildren-1);
size_t subtree_size = 1; // account for root node
size_t child_size = 0; // size/esize of a child subtrees
size_t csize[4]; // sizes/esizes of the children subtrees
size_t cpos = 0; // current position in size[]
for(int i=0;i<4;i++)
if(root & (1<<i)) {
if(size==2*MMsize) // end of recursion
*pos += (child_size = Minimat_node_ratio);
else { // recurse on submatrix
child_size = k2dfs_sizes(size/2,m,pos,z,depth2go-1); // read submatrix and advance pos
}
#ifdef CHECK_ESIZE_OVERFLOW
// save size and esize for possible later storage in z
csize[cpos++] = child_size;
// sum sizes and esizes, check for possible overflow
if(__builtin_add_overflow(subtree_size,child_size,&subtree_size))
quit("Overflow in subtree encoding: make BITSxTSIZE smaller if possible",__LINE__,__FILE__);
#else
subtree_size += (csize[cpos++] = child_size); // save and sum sizes and esizes, no check
#endif
}
assert(cpos==nchildren); // we should have visited all children
// add subtree size for all children except last one
if(depth2go>0) {
for(int i=0; i<cpos-1; i++)
z->v[zn_save++] = csize[i];
#ifdef CHECK_ESIZE_OVERFLOW
if(__builtin_add_overflow(subtree_size,(nchildren-1)<<BITSxTSIZE,&subtree_size))
quit("Overflow in subtree encoding: make BITSxTSIZE smaller if possible",__LINE__,__FILE__);
#else
subtree_size += (nchildren-1)<<BITSxTSIZE;
#endif
}
else assert(subtree_size>>BITSxTSIZE == 0); // there should not be any subtree encoding
if(*pos != pos_save + (subtree_size&TSIZEMASK)) { // double check size
fprintf(stderr,"Scanned size: %llu, computed size: %llu\n", (unsigned long long)(*pos-pos_save), (unsigned long long)(subtree_size&TSIZEMASK));
quit("Error or overflow in size encoding",__LINE__,__FILE__);
}
return subtree_size;
}
#endif
// compute subtree information as above, but information is stored only
// for large trees, ie when the number of nodes is larger than :limit
#ifdef SIMPLEBACKPOINTERS
uint64_t k2dfs_sizes_limit(size_t size, const k2mat_t *m, size_t *pos, vu64_t *z, size_t limit)
{
assert(size>MMsize);
assert(size%2==0);
assert(*pos<m->pos); // implies m is non-empty
size_t pos_save = *pos; // save starting position of subtree
node_t root = k2read_node(m,*pos); (*pos)++;
assert(root<ILLEGAL_NODE);
if(root==ALL_ONES) // all 1's matrix consists of root only,
return 1; // size is 1, subtree encoding size 0
// we have a non-singleton subtree to traverse
// compute number of children
size_t nchildren = __builtin_popcountll(root);
assert(nchildren>0 && nchildren<=4);
if(size==2*MMsize) { // end of recursion
*pos += nchildren*Minimat_node_ratio;
return 1 + nchildren*Minimat_node_ratio; // size, subtree encoding size 0
}
size_t zn_save = z->n; // save starting position in size_array[]
vu64_grow(z,nchildren-1); // reserve space for children
size_t subtree_size = 1; // account for root node
size_t child_size = 0; // size/esize of a child subtrees
size_t csize[4]; // sizes/esizes of the children subtrees
size_t cpos = 0; // current position in size[]
for(int i=0;i<4;i++)
if(root & (1<<i)) {
// recurse on submatrix
child_size = k2dfs_sizes_limit(size/2,m,pos,z,limit); // read submatrix and advance pos
assert(child_size>0);
// save size and esize for possible later storage in z
csize[cpos++] = child_size;
// add sizes and esizes to subtree_size, check for possible overflow
if(__builtin_add_overflow(subtree_size,child_size,&subtree_size))
quit("Overflow in subtree encoding: make BITSxTSIZE smaller if possible",__LINE__,__FILE__);
}
assert(cpos==nchildren); // we should have visited all children
// check subtree size for all children except last one
if((subtree_size&TSIZEMASK)>limit) {
for(int i=0; i<cpos-1; i++)
z->v[zn_save++] = csize[i];
// add nchildren-1 to the encoding size, checking for overflow
if(__builtin_add_overflow(subtree_size,(nchildren-1)<<BITSxTSIZE,&subtree_size))
quit("Overflow in subtree encoding: make BITSxTSIZE smaller if possible",__LINE__,__FILE__);
}
else {
assert(subtree_size>>BITSxTSIZE == 0); // there should not be any subtree encodings
assert(z->n == zn_save+nchildren-1); // no subtree information stored
z->n = zn_save; // no subtree information
}
if(*pos != pos_save + (subtree_size&TSIZEMASK)) { // double check size
fprintf(stderr,"Scanned size: %zu, computed size: %llu\n", *pos-pos_save,(unsigned long long)(subtree_size&TSIZEMASK));
quit("Error or overflow in size encoding",__LINE__,__FILE__);
}
return subtree_size;
}
#else
// NEW: version storing the info also for the last child
uint64_t k2dfs_sizes_limit(size_t size, const k2mat_t *m, size_t *pos, vu64_t *z, size_t limit)
{
assert(size>MMsize);
assert(size%2==0);
assert(*pos<m->pos); // implies m is non-empty
size_t pos_save = *pos; // save starting position of subtree
node_t root = k2read_node(m,*pos); (*pos)++;
assert(root<ILLEGAL_NODE);
if(root==ALL_ONES) // all 1's matrix consists of root only,
return 1; // size is 1, subtree encoding size 0
// we have a non-singleton subtree to traverse
// compute number of children
size_t nchildren = __builtin_popcountll(root);
assert(nchildren>0 && nchildren<=4);
size_t zn_save = z->n; // save starting position in size_array[]
vu64_grow(z,nchildren);
size_t subtree_size = 1; // account for root node
size_t child_size = 0; // size/esize of a child subtrees
size_t csize[4]; // sizes/esizes of the children subtrees
size_t cpos = 0; // current position in size[]
for(int i=0;i<4;i++)
if(root & (1<<i)) {
if(size==2*MMsize) // end of recursion
*pos += (child_size = Minimat_node_ratio);
else { // recurse on submatrix
child_size = k2dfs_sizes_limit(size/2,m,pos,z,limit); // read submatrix and advance pos
}
// save size and esize for possible later storage in z
csize[cpos++] = child_size;
// add sizes and esizes to subtree_size, check for possible overflow
if(__builtin_add_overflow(subtree_size,child_size,&subtree_size))
quit("Overflow in subtree encoding: make BITSxTSIZE smaller if possible",__LINE__,__FILE__);
}
assert(cpos==nchildren); // we should have visited all children
// check subtree size for all children including the last one
if((subtree_size&TSIZEMASK)>limit) {
// complete encodig for current subtree with the x child info
for(int i=0; i<cpos; i++)
z->v[zn_save++] = csize[i];
// add nchildren to the encoding size, checking for overflow
if(__builtin_add_overflow(subtree_size,(nchildren)<<BITSxTSIZE,&subtree_size))
quit("Overflow in subtree encoding: make BITSxTSIZE smaller if possible",__LINE__,__FILE__);
}
else {
assert(subtree_size>>BITSxTSIZE == 0); // there should not be any subtree encodings
assert(z->n == zn_save+nchildren); // no subtree information stored
z->n = zn_save; // no subtree information
}
if(*pos != pos_save + (subtree_size&TSIZEMASK)) { // double check size
fprintf(stderr,"Scanned size: %zu, computed size: %llu\n", *pos-pos_save,(unsigned long long)(subtree_size&TSIZEMASK));
quit("Error or overflow in size encoding",__LINE__,__FILE__);
}
return subtree_size;
}
#endif
// do a dfs visit of the k2 matrix :m and make sure subtree sizes match the ones in :z
// the checking is done recursively, but as soon as the encoding of a subtree
// has length 0, that subtree is explored with a fast dfs visit that only reports
// the subtree size.
// In the code below we call "tree" the one we are exploring (representing :m)
// and "subtrees" its immediate descendant
// Recall that if the tree has 3 non empty children
// its encoding consists of
// <T1> <Sub1> <T2> <Sub2> Sub1 Sub2 Sub3 (where <x> denotes size of x)
// We compare <T1> and <T2> with the size returned from the subtree visits
// <T3> is not stored so it is checked at the upper level where
// 1 + <T1> + <T2> + <T3> will be compared with the size stored for
// T's parent. <Sub1> and <Sub2> are tested with the amount of data
// scanned during the visit of T1 and T2. The value <Sub3> is obtained as
// <Sub3> = tot_encode_size - <<T1> <Sub1> <T2><Sub2>> - <Sub1> - <Sub2>
// and is compared with the amount of data scanned during the visit of T3
// Parameters:
// size internal size of the current submatrix
// m,*pos the current submatrix starts at position *pos within *m
// z dynamic vector where the subtree information to be checked is stored
// tot_encode_size total size of the encoding (info in z) for tree (and subtrees)
// as obtained at the previous level (T's parent)
// Note: it is assumed that the root m[*pos] node has associate subtinfo in z[z->n]
// Return:
// size (number of nodes) of T (hence not including the info in z)
#ifdef SIMPLEBACKPOINTERS
size_t k2dfs_check_sizes(size_t size, const k2mat_t *m, size_t *pos, vu64_t *z,
size_t tot_encode_size)
{
assert(size>MMsize);
assert(size%2==0);
assert(*pos<m->pos); // implies m is non-empty
size_t pos_save = *pos; // save starting position of tree
node_t root = k2read_node(m,*pos); (*pos)++;
assert(root<ILLEGAL_NODE);
if(root==ALL_ONES) // all 1's matrix consists of root only,
return 1; // tree size is 1 no subtree info to check
// we have a non-singleton tree T to traverse
// compute number of children
size_t nchildren = __builtin_popcountll(root);
assert(nchildren>0 && nchildren<=4);
// read subtree information if available
uint64_t *subtree_info = &(z->v[z->n]); // array with subtree_info information
z->n += nchildren-1; // advance z->n to the subtree encoding area
size_t encode_seen = nchildren-1; // consume one item x non-last children
// visit children
size_t cnum = 0; // current child
size_t tree_size = 1; // account for root node
for(int i=0;i<4;i++)
// invariant: both *pos and z->n point to the beginning of subtree cnum
if(root & (1<<i)) {
size_t pc = *pos; // save current position in m and z
size_t nc = z->n;
size_t child_subtree_size=0, child_encode_size = 0;
// compute size of subtree encoding
if(cnum<nchildren-1) {
child_encode_size = subtree_info[cnum]>>BITSxTSIZE;
encode_seen += subtree_info[cnum]>>BITSxTSIZE;
}
else // last child
child_encode_size = tot_encode_size -encode_seen; // remaining encoding
// ------- go down one level -----------
if(size==2*MMsize) // end of recursion
*pos += (child_subtree_size = Minimat_node_ratio); // update *pos and child_subtree_size
else if(child_encode_size==0) {
k2dfs_visit_fast(size/2,m,pos); // advance pos to the end of subtree
child_subtree_size = *pos -pc; // recover subtree size from advancement in *pos
}
else {// recurse on subtree
child_subtree_size = k2dfs_check_sizes(size/2,m,pos,z,child_encode_size);
// check that child_subtree_size matches the advancement in *pos
if(child_subtree_size != *pos -pc)
fprintf(stderr,"Subtree scanned size: %zu, reported size: %zu\n",*pos-pc,child_subtree_size);
}
// if not last child check that stored subtree size matches
if(cnum<nchildren-1 && child_subtree_size!=(subtree_info[cnum]&TSIZEMASK))
fprintf(stderr,"Subtree stored size: %zu, reported size: %zu\n",subtree_info[cnum]&TSIZEMASK,child_subtree_size);
// check stored subtree encoding matches
size_t scanned_encoding = z->n-nc;
if(child_encode_size!=scanned_encoding) {
if(cnum<nchildren-1)
fprintf(stderr,"Subtree encoding stored size: %zu, scanned size: %zu\n",child_encode_size,scanned_encoding);
else
fprintf(stderr,"Subtree encoding computed size: %zu, scanned size: %zu\n",child_encode_size,scanned_encoding);
}
cnum++;
tree_size += child_subtree_size;
}
assert(cnum==nchildren); // we should have visited all children
assert(*pos == pos_save + tree_size); // check again tree size
(void) pos_save; // avoid warning
return tree_size;
}
#else
// alternative version in which the subtree size is stored also for the last child
size_t k2dfs_check_sizes(size_t size, const k2mat_t *m, size_t *pos, vu64_t *z,
size_t tot_encode_size)
{
assert(size>MMsize);
assert(size%2==0);
assert(*pos<m->pos); // implies m is non-empty
size_t pos_save = *pos; // save starting position of tree
node_t root = k2read_node(m,*pos); (*pos)++;
assert(root<ILLEGAL_NODE);
if(root==ALL_ONES) // all 1's matrix consists of root only, (this case should not happen)
return 1; // tree size is 1 no subtree info to check
// we have a non-singleton tree T to traverse
// compute number of children
size_t nchildren = __builtin_popcountll(root);
assert(nchildren>0 && nchildren<=4);
// read subtree information
uint64_t *subtree_info = &(z->v[z->n]); // array with subtree_info information
z->n += nchildren; // advance z->n to the subtree encoding area
size_t encode_seen = nchildren; // consume one item for each children
// visit children
size_t cnum = 0; // current child
size_t tree_size = 1; // account for root node
for(int i=0;i<4;i++)
// invariant: both *pos and z->n point to the beginning of subtree cnum
if(root & (1<<i)) {
size_t pc = *pos; // save current position in m and z
size_t nc = z->n;
size_t child_subtree_size=0;
// compute size of subtree encoding
size_t child_encode_size = subtree_info[cnum]>>BITSxTSIZE;
encode_seen += child_encode_size;
// ------- go down one level -----------
if(size==2*MMsize) { // end of recursion
assert(child_encode_size==0); // for hieght 2 nodes there canot be a subtree encoding
*pos += (child_subtree_size = Minimat_node_ratio); // update *pos and child_subtree_size
} else if(child_encode_size==0) { // no subtree encoding, scan subtree with dfs
k2dfs_visit_fast(size/2,m,pos); // advance pos to the end of subtree
child_subtree_size = *pos -pc; // recover subtree size from advancement in *pos
}
else {// recurse on subtree
child_subtree_size = k2dfs_check_sizes(size/2,m,pos,z,child_encode_size);
// check that child_subtree_size matches the advancement in *pos
if(child_subtree_size != *pos -pc)
fprintf(stderr,"Subtree scanned size: %zu, reported size: %zu\n",*pos-pc,child_subtree_size);
}
// check that stored subtree size matches
if(child_subtree_size!=(subtree_info[cnum]&TSIZEMASK))
fprintf(stderr,"Subtree stored size: %zu, reported size: %zu\n",subtree_info[cnum]&TSIZEMASK,child_subtree_size);
// check stored subtree encoding matches
size_t scanned_encoding = z->n-nc;
if(child_encode_size!=scanned_encoding)
fprintf(stderr,"Subtree encoding stored size: %zu, scanned size: %zu\n",child_encode_size,scanned_encoding);
cnum++;
tree_size += child_subtree_size;
}
assert(cnum==nchildren); // we should have visited all children
assert(*pos == pos_save + tree_size); // check again tree size
assert(encode_seen == tot_encode_size); // check that we have seen all subtree encodings
(void) pos_save; // avoid warning
(void) tot_encode_size;
return tree_size;
}
#endif
// similar to k2dfs_check_sizes(), but instead of checking the subtree sizes
// for each node which is destination of a backpointer, we start???? the position
// of each correponding subtree info together with the backpointer
void k2dfs_compute_backpointer_info(size_t size, const k2mat_t *m, size_t *pos, vu64_t *z)
{
#ifdef SIMPLEBACKPOINTERS
quit("k2dfs_compute_backpointer_info: should not be used for simple backpointers", __LINE__, __FILE__);
#else
assert(size>MMsize);
assert(size%2==0);
assert(*pos<m->pos); // implies m is non-empty
assert(z->n < z->nmax);
node_t root = k2read_node(m,*pos);
assert(root<ILLEGAL_NODE);
if(*pos > TSIZEMASK) quit("k2dfs_backpointer_info: *pos overflow",__LINE__,__FILE__);
if(root==ALL_ONES) // all 1's matrix consists of root only
return; // nothing to do (this case should not happen unless the whole matrix is ALL_ONES)
// we have a non-singleton tree T with root in *pos and subtring in z->v[z->n]
// if pos is destination of a backpointer, the corresponding backpointer
// is enriched with the value z->n
pointers_t *ps = m->backp; // backpointer structure
// skip values in ps->sorted until we reach the first value >= *pos
while(ps->sidx<ps->size && *pos > ps->nodep[ps->sorted[ps->sidx]])
ps->sidx += 1; // skip all entries smaller than *pos
while(ps->sidx<ps->size && *pos == ps->nodep[ps->sorted[ps->sidx]]) {
ps->nodep[ps->sorted[ps->sidx]] |= z->n << BITSxTSIZE; // store the backpointer in z->n
// uncompress the following line if you want to see the backpointer subtree info on stderr
//fprintf(stderr,"For node %zu the subtree info is in %zu first size %zu root:%zu, fc %zu\n",*pos, z->n,z->v[z->n]&TSIZEMASK,k2read_node(m,*pos),k2read_node(m,*pos+1));
ps->sidx += 1;
}
// now recurse on the subtrees of T
// compute number of children
(*pos)++;
size_t nchildren = __builtin_popcountll(root);
assert(nchildren>0 && nchildren<=4);
// read subtree information
uint64_t *subtree_info = &(z->v[z->n]); // array with subtree_info information
z->n += nchildren; // advance z->n to the subtree encoding area
// visit children
size_t cnum = 0; // current child
for(int i=0;i<4;i++)
// invariant: both *pos and z->n point to the beginning of subtree cnum
if(root & (1<<i)) {
// compute size of subtree encoding
size_t child_encode_size = subtree_info[cnum]>>BITSxTSIZE;
// ------- go down one level -----------
if(size==2*MMsize) // end of recursion
*pos += Minimat_node_ratio; // quickly update *pos
else if(child_encode_size==0) // no subtree encoding, scan subtree with dfs
k2dfs_visit_fast(size/2,m,pos); // advance pos to the end of subtree
else // recurse on subtree
k2dfs_compute_backpointer_info(size/2,m,pos,z);
cnum++;
}
assert(cnum==nchildren); // we should have visited all children
#endif
}
// ----------- static auxiliary functions ------------
// compress the matrix of size msize represented by the interleaved
// array ia[0..n-1] into the k2mat_t structure *a
// ia[] should be an interleaved array of length n
// the old content of :a is lost
// return the size of the k2 matrix (which has the form 2**k*MMsize)
// make sure that all entries are distinct (another option would be to
// just remove duplicates)
// since entries are encoded in 64 bits, each index can be at most 32 bits
// so the maximum matrix size is 2^32 (change ia[] type to go further)
static size_t mread_from_ia(uint64_t ia[], size_t n, size_t msize, k2mat_t *a)
{
assert(ia!=NULL && a!=NULL);
assert(n>0); // we cannot represent an empty matrix
assert(msize>1);
assert(a_eq_b2(n,msize) || a_lt_b2(n,msize)); // entries can be at most msize**2
k2_free(a); // free previous content of a
if(msize>1ULL+UINT32_MAX) quit("mread_from_ia: matrix too large, current limit is 2^32",__LINE__,__FILE__);
size_t asize = k2get_k2size(msize);
assert(asize>=2*MMsize);
// count duplicates
size_t dup=0;
for(size_t i=1;i<n;i++)
if(ia[i-1]==ia[i]) dup++;
if(dup>0) {
fprintf(stderr,"Input file contains %zu duplicate entries\n",dup);
exit(EXIT_FAILURE);
}
// encode ia[0,n-1] into the k2mat_t structure a
mencode_ia(ia,n,0,asize,a);
return asize;
}
// compare a and b^2 with only operations
// involving uint64_t and without overflow
static inline bool a_eq_b2(uint64_t a, uint64_t b)
{
return (a/b==b) ? (a%b==0) : false;
}
static inline bool a_lt_b2(uint64_t a, uint64_t b)
{
return (a/b<b);
}
// given a sorted uint64_t array ia[0,n-1] containing distinct values find
// the first entry >= x using binary search
// return n if no such entry exists
static size_t binsearch(uint64_t *ia, size_t n, uint64_t x) {
assert(ia!=NULL && n>0);
size_t l=0, r=n-1;
while(l<r) {
size_t m = (l+r)/2;
if(ia[m]<x) l=m+1;
else if(ia[m]==x) return m;
else r=m; // replace with r = m-1 and later return r+1?
}
assert(l==r);
if(ia[l]<x) {
assert(r==n-1);
return n; // replace with return r+1?
}
return l;
}
// recursively encode a submatrix in interleaved format
// into a k2mat_t structure
// Parameters:
// ia[0,n-1] array containing the distinct interleaved entries
// smin smallest value assigned to the current submatrix
// size submatrix size (has the form 2^k*MMsize)
// *c output k2mat_t structure to be filled in dfs order
// all entries in ia[0,n-1] are in the range [smin, smin+size*size)
// all these entries must be encoded in the k2mat c
// In previous versions of the code also the parameter imax = smin+size^2
// was used explicitly: it has been removed since for size==2^32
// such value could be 2^64 and therefore not representable in a uint64
// called by mread_from_ia()
static void mencode_ia(uint64_t *ia, size_t n, uint64_t smin, size_t size, k2mat_t *c) {
//printf("Size=%zu, n=%zu, smin=%lu\n",size,n,smin);
assert(ia!=NULL);
assert(n>0);
assert(ia[0]>=smin);
// assert(ia[n-1]<smin+size*size); replaced by the following line
assert( a_lt_b2(ia[n-1]-smin, size));
assert(size%2==0 && size>=2*MMsize);
// case of a full submatrix
if(a_eq_b2(n,size) && Use_all_ones_node) { // equivalent to (n==size*size) but no overflow
k2add_node(c,ALL_ONES); // submatrix is full
return;
}
// determine range of submatrices
assert(size/2<UINT32_MAX); // check that size/2 can be squared without overflow
uint64_t range = (size/2)*(size/2);
uint64_t left = smin + range;
uint64_t mid = left+range;
uint64_t right = mid+range;
// printf("range=%lu imax-smin=%lu\n",range,right+range-smin);
if(size==1ULL+UINT32_MAX) // max value size=2^32 treated separately
assert(right-smin>0 && right-smin+range==0); // equiv to right-smin+range==2^64
else
assert(a_eq_b2(right-smin+range,size)); // equiv to: right+range == smin + size^2
// determine range in ia[] of the 4 submatrices entries
size_t imid = binsearch(ia,n,mid); // first entry of A[10]
size_t ileft = imid>0 ? binsearch(ia,imid,left):0; // first entry of A[01]
size_t iright = imid<n? binsearch(ia+imid,n-imid,right)+imid:n; // first entry of A[11]
// the four submatrices are:
// ia[0,ileft-1], ia[ileft,imid-1], ia[imid,iright-1], ia[iright,n-1]
// and contain values in the ranges
// [smin,left), [left,mid), [mid,right), [right,smin+size^2)
// start building c
size_t rootpos = k2add_node(c,ALL_ONES); // write ALL_ONES as root placeholder
node_t rootc=NO_CHILDREN; // actual root node to be computed
// here we are assuming that the submatrices are in the order 00,01,10,11
if(ileft>0) { // submatrix 00 is not empty
rootc |= (1<<0); // set 00 bit
if(size>2*MMsize) // if size>2*MMsize recurse
mencode_ia(ia,ileft,smin,size/2,c);
else { // size==2*MMsize: write a minimatrix
minimat_t cx = minimat_from_ia(ia,ileft,smin,size/2);
k2add_minimat(c,cx);
}
}
if(ileft<imid) { // submatrix 01 is not empty
rootc |= (1<<1); // set 01 bit
if(size>2*MMsize) // if size>2*MMsize recurse
mencode_ia(ia+ileft,imid-ileft,left,size/2,c);
else { // size==2*MMsize: write a minimatrix
minimat_t cx = minimat_from_ia(ia+ileft,imid-ileft,left,size/2);
k2add_minimat(c,cx);
}
}
if(iright>imid) { // submatrix 10 is not empty
rootc |= (1<<2); // set 10 bit
if(size>2*MMsize) // if size>2*MMsize recurse
mencode_ia(ia+imid,iright-imid,mid,size/2,c);
else { // size==2*MMsize: write a minimatrix
minimat_t cx = minimat_from_ia(ia+imid,iright-imid,mid,size/2);
k2add_minimat(c,cx);
}
}
if(iright<n) { // submatrix 11 is not empty
rootc |= (1<<3); // set 11 bit
if(size>2*MMsize) // if size>2*MMsize recurse
mencode_ia(ia+iright,n-iright,right,size/2,c);
else { // size==2*MMsize: write a minimatrix
minimat_t cx = minimat_from_ia(ia+iright,n-iright,right,size/2);
k2add_minimat(c,cx);
}
}
assert(rootc!=NO_CHILDREN); // at least one submatrix is not empty
k2write_node(c,rootpos,rootc); // fix root
}
// interleaves two 32 bits integers in a single uint64_t
// the bits of a (row index) are more significant than
// those of b (column index) because of how we number submatrices
static uint64_t bits_interleave(int64_t a, int64_t b)
{
uint64_t r = 0;
assert(a<=UINT32_MAX && b <= UINT32_MAX);
int c = 0;
while(a!=0 || b!=0) {
r |= (b&1)<<c++;
r |= (a&1)<<c++;
a >>= 1; b>>=1;
assert(c<=64);
}
return r;
}
static int uint64_cmp(const void *p, const void *q)
{
const uint64_t *a = p;
const uint64_t *b = q;
if(*a < *b) return -1;
else if(*a > *b) return 1;
return 0;
}
// create and return an interleaved array from the list of entries in a text file
// the matrix size stored in :msize is computed as follows:
// if xsize==0 *msize = largest index + 1
// if xsize>0 that value is forced to be the matrix size (all indexes must be <xsize)
// since entries are encoded in 64 bits, each index can be at most 32 bits
// so the maximum matrix size is 2^32 (change ia[] type to go further)
static uint64_t *create_ia(FILE *f, size_t *n, size_t *msize, size_t xsize)
{
int64_t maxentry = 0; // largest entry in the file
size_t size=10; // current size of ia[]
size_t i=0; // elements in ia[]
uint64_t *ia = malloc(size*sizeof(*ia));
if(ia==NULL) quit("create_ia: malloc failed",__LINE__,__FILE__);
int64_t a,b; size_t line=0;
while(true) {
line++;
int e = fscanf(f,"%" SCNd64 " %" SCNd64,&a,&b);
if(e==EOF) break;
// check input
if(e!=2) {
fprintf(stderr,"Invalid file content at line %zu\n",line);
exit(EXIT_FAILURE);
}
if(a<0 || b<0) {
fprintf(stderr,"Negative index at line %zu\n",line);
exit(EXIT_FAILURE);
}
// since we are storing entries in 64 bits each index must fit in 32 bits
if(a>UINT32_MAX || b>UINT32_MAX) {
fprintf(stderr,"Index too large at line %zu\n",line);
exit(EXIT_FAILURE);
}
if(xsize>0 && (a>=xsize || b>=xsize)) {
fprintf(stderr,"Index larger than the assigned size at line %zu\n",line);
exit(EXIT_FAILURE);
}
// update maxentry
if(a>maxentry) maxentry=a;
if(b>maxentry) maxentry=b;
// compute interleaved value
uint64_t entry = bits_interleave(a,b);
// enlarge ia if necessary
if(i==size) {
size = size*2;
ia = realloc(ia,size*sizeof(*ia));
if(ia==NULL) quit("create_ia: realloc failed",__LINE__,__FILE__);
}
assert(size>i);
ia[i++] = entry;
}
// final resize
size = i;
ia = realloc(ia,size*sizeof(*ia));
if(ia==NULL) quit("create_ia: realloc failed",__LINE__,__FILE__);
// sort interleaved entries
qsort(ia, size, sizeof(*ia), &uint64_cmp);
// save output parameters
if(xsize==0) { // if xsize==0 size is largest index + 1
if(maxentry+1>SIZE_MAX) // highly unlikely, but you never know...
quit("create_ia: cannot represent matrix size",__LINE__,__FILE__);
*msize = (size_t) maxentry+1;
}
else { // if parameter xsize>0 that is the desired matrix size
assert(maxentry<xsize);
*msize = xsize;
}
*n = size;
return ia;
}
// -----------------------------
// decode to sparse text format a matrix of size 2*MMsize
static void mdecode_to_textfile_base(FILE *outfile, size_t msize, size_t i, size_t j, size_t size, const k2mat_t *a, size_t *pos)
{
assert(size==2*MMsize);
assert(a!=NULL);
assert(!k2is_empty(a));
assert(!a->main_diag_1 || i==j); // if main_diag_1 is on we must be on a diagonal submatrix
minimat_t ax[2][2];
// read root of a
node_t roota = k2read_node(a,*pos); *pos += 1;
if(roota==ALL_ONES) {
if(a->backp!=NULL) quit("Illegal matrix: has backpointers and an ALL_ONES node at last level",__LINE__,__FILE__);
// output all 1s submatrix main_diag_1 irrelevant
for(size_t ii=i; ii<i+size && ii < msize; ii++)
for(size_t jj=j; jj<j+size && jj < msize; jj++) {
int e = fprintf(outfile,"%zu %zu\n",ii,jj);
if(e<0) quit("mdecode_to_textfile_base: fprintf failed",__LINE__,__FILE__);
}
}
else {
// split :a taking care also of main_diag
k2split_minimats(a,pos,roota,ax); // not we cannot pass here i,j
// fprintf(stderr,"Decoding base submatrix i=%zu j=%zu %x %x %x %x\n",i,j,ax[0][0],ax[0][1],ax[1][0],ax[1][1]);
for(size_t k=0;k<4;k++) {
size_t ii = i + (size/2)*(k/2); size_t jj= j + (size/2)*(k%2);
minimat_to_text(outfile,msize,ii,jj,size/2, ax[k/2][k%2]);
}
}
}
// recursively decode a k2 submatrix into a list of entries written to a text file
// Parameters:
// f output file
// msize actual file of the matrix
// i,j submatrix top left corner
// size k2 submatrix size (has the form 2^k*MMsize)
// *c input k2mat_t structure
// *pos position in *c where the submatrix starts
// Note: subtinfo is not used here. if backp!=NULL and a POINTER/ALL_ONES node is found it is followed
// when creating the output if backp==NULL then ALL_ONES nodes are treated as full 1s submatrices
static void mdecode_to_textfile(FILE *outfile, size_t msize, size_t i, size_t j, size_t size, const k2mat_t *c, size_t *pos)
{
assert(!k2is_empty(c)); // never called on an empty matrix
assert(c->offset==0); // we are never working on submatrices
assert(size%2==0 && size>=2*MMsize);
assert(i%MMsize==0 && j%MMsize==0);
// assert(i<msize+2*size && j<msize+2*size); // I can remember why the +2*size was needed, see following line
assert(i<msize && j<msize);
assert(!c->main_diag_1 || i==j); // if main_diag_1 is on we must be on a diagonal submatrix
if(size==2*MMsize) { // base case
mdecode_to_textfile_base(outfile,msize,i,j,size,c,pos);
return;
}
// size>2*MMsize and c contains some data: read c root
node_t rootc=k2read_node(c,*pos); *pos +=1;
if(c->backp==NULL && rootc==ALL_ONES) { // all 1s matrix
// output all 1s submatrix, main_diag_1 irrelevant
for(size_t ii=i; ii<i+size && ii < msize; ii++)
for(size_t jj=j; jj<j+size && jj < msize; jj++) {
int e = fprintf(outfile,"%zu %zu\n",ii,jj);
if(e<0) quit("mdecode_to_textfile: fprintf failed",__LINE__,__FILE__);
}
return;
}
// if pointer node follow it by simply changing position
if(c->backp!=NULL && rootc==POINTER) { // recall POINTER=ALL_NODES=0000
k2pointer_t destp = k2get_backpointer(c,*pos-1); // -1 because we have already advanced pos
size_t posp = destp; // move position to the target subtree
mdecode_to_textfile(outfile,msize,i,j,size,c,&posp);
return;
}
// general case: not a pointer node and there is at least one child
// here we are assuming that the submatrices are in the order 00,01,10,11