1 /*
2  # This file is part of libkd.
3  # Licensed under a 3-clause BSD style license - see LICENSE
4  */
5 
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <math.h>
10 #include <stdint.h>
11 #include <strings.h>
12 #include <errno.h>
13 
14 #include "cutest.h"
15 #include "kdtree.h"
16 #include "kdtree_fits_io.h"
17 
18 #include "test_libkd_common.c"
19 
assert_kdtrees_equal(CuTest * ct,const kdtree_t * kd,const kdtree_t * kd2)20 static void assert_kdtrees_equal(CuTest* ct, const kdtree_t* kd, const kdtree_t* kd2) {
21     double del = 1e-10;
22     size_t sz, sz2;
23 
24     if (!kd) {
25         CuAssertPtrEquals(ct, NULL, (kdtree_t*)kd2);
26         return;
27     }
28     CuAssertPtrNotNull(ct, kd2);
29 
30     CuAssertIntEquals(ct, kd->treetype, kd2->treetype);
31     CuAssertIntEquals(ct, kd->dimbits, kd2->dimbits);
32     CuAssertIntEquals(ct, kd->dimmask, kd2->dimmask);
33     CuAssertIntEquals(ct, kd->splitmask, kd2->splitmask);
34     CuAssertIntEquals(ct, kd->ndata, kd2->ndata);
35     CuAssertIntEquals(ct, kd->ndim, kd2->ndim);
36     CuAssertIntEquals(ct, kd->nnodes, kd2->nnodes);
37     CuAssertIntEquals(ct, kd->nbottom, kd2->nbottom);
38     CuAssertIntEquals(ct, kd->ninterior, kd2->ninterior);
39     CuAssertIntEquals(ct, kd->nlevels, kd2->nlevels);
40     CuAssertIntEquals(ct, kd->has_linear_lr, kd2->has_linear_lr);
41     CuAssertDblEquals(ct, kd->scale,    kd2->scale,    del);
42     CuAssertDblEquals(ct, kd->invscale, kd2->invscale, del);
43 
44     if (kd->lr) {
45         CuAssertPtrNotNull(ct, kd2->lr);
46         sz  = kdtree_sizeof_lr(kd );
47         sz2 = kdtree_sizeof_lr(kd2);
48         CuAssertIntEquals(ct, sz, sz2);
49         CuAssert(ct, "lr equal", memcmp(kd->lr, kd2->lr, sz) == 0);
50     } else {
51         CuAssertPtrEquals(ct, NULL, kd2->lr);
52     }
53 
54     if (kd->perm) {
55         CuAssertPtrNotNull(ct, kd2->perm);
56         sz  = kdtree_sizeof_perm(kd );
57         sz2 = kdtree_sizeof_perm(kd2);
58         CuAssertIntEquals(ct, sz, sz2);
59         CuAssert(ct, "perm equal", memcmp(kd->perm, kd2->perm, sz) == 0);
60     } else {
61         CuAssertPtrEquals(ct, NULL, kd2->perm);
62     }
63 
64     if (kd->data.any) {
65         CuAssertPtrNotNull(ct, kd2->data.any);
66         sz  = kdtree_sizeof_data(kd );
67         sz2 = kdtree_sizeof_data(kd2);
68         CuAssertIntEquals(ct, sz, sz2);
69         CuAssert(ct, "data equal", memcmp(kd->data.any, kd2->data.any, sz) == 0);
70     } else {
71         CuAssertPtrEquals(ct, NULL, kd2->data.any);
72     }
73 
74     if (kd->splitdim) {
75         CuAssertPtrNotNull(ct, kd2->splitdim);
76         sz  = kdtree_sizeof_splitdim(kd );
77         sz2 = kdtree_sizeof_splitdim(kd2);
78         CuAssertIntEquals(ct, sz, sz2);
79         CuAssert(ct, "splitdim equal", memcmp(kd->splitdim, kd2->splitdim, sz) == 0);
80     } else {
81         CuAssertPtrEquals(ct, NULL, kd2->splitdim);
82     }
83 
84     if (kd->split.any) {
85         CuAssertPtrNotNull(ct, kd2->split.any);
86         sz  = kdtree_sizeof_split(kd );
87         sz2 = kdtree_sizeof_split(kd2);
88         CuAssertIntEquals(ct, sz, sz2);
89         CuAssert(ct, "split equal", memcmp(kd->split.any, kd2->split.any, sz) == 0);
90     } else {
91         CuAssertPtrEquals(ct, NULL, kd2->split.any);
92     }
93 
94     if (kd->bb.any) {
95         CuAssertPtrNotNull(ct, kd2->bb.any);
96         sz  = kdtree_sizeof_bb(kd );
97         sz2 = kdtree_sizeof_bb(kd2);
98         CuAssertIntEquals(ct, sz, sz2);
99         CuAssert(ct, "bb equal", memcmp(kd->bb.any, kd2->bb.any, sz) == 0);
100     } else {
101         CuAssertPtrEquals(ct, NULL, kd2->bb.any);
102     }
103 
104     if (kd->minval) {
105         sz  = kd->ndim * sizeof(double);
106         CuAssertPtrNotNull(ct, kd2->minval);
107         CuAssert(ct, "minval equal", memcmp(kd->minval, kd2->minval, sz) == 0);
108     } else {
109         CuAssertPtrEquals(ct, NULL, kd2->minval);
110     }
111 
112     if (kd->maxval) {
113         CuAssertPtrNotNull(ct, kd2->maxval);
114         sz  = kd->ndim * sizeof(double);
115         CuAssert(ct, "maxval equal", memcmp(kd->maxval, kd2->maxval, sz) == 0);
116     } else {
117         CuAssertPtrEquals(ct, NULL, kd2->maxval);
118     }
119 
120     if (kd->name) {
121         CuAssertPtrNotNull(ct, kd2->name);
122         CuAssert(ct, "name equal", strcmp(kd->name, kd2->name) == 0);
123     } else {
124         CuAssertPtrEquals(ct, NULL, kd2->name);
125     }
126 }
127 
test_read_write_single_tree_unnamed(CuTest * ct)128 void test_read_write_single_tree_unnamed(CuTest* ct) {
129     kdtree_t* kd;
130     double * data;
131     int N = 1000;
132     int Nleaf = 5;
133     int D = 3;
134     char fn[1024];
135     int rtn;
136     kdtree_t* kd2;
137     int fd;
138 
139     data = random_points_d(N, D);
140     kd = build_tree(ct, data, N, D, Nleaf, KDTT_DOUBLE, KD_BUILD_SPLIT);
141     kd->name = NULL;
142 
143     sprintf(fn, "/tmp/test_libkd_io_single_tree_unnamed.XXXXXX");
144     fd = mkstemp(fn);
145     if (fd == -1) {
146         fprintf(stderr, "Failed to generate a temp filename: %s\n", strerror(errno));
147         CuFail(ct, "mkstemp");
148     }
149     close(fd);
150     printf("Single tree unnamed: writing to file %s.\n", fn);
151 
152     rtn = kdtree_fits_write(kd, fn, NULL);
153     CuAssertIntEquals(ct, 0, rtn);
154 
155     kd2 = kdtree_fits_read(fn, NULL, NULL);
156 
157     assert_kdtrees_equal(ct, kd, kd2);
158 
159     free(data);
160     kdtree_free(kd);
161     kdtree_fits_close(kd2);
162 }
163 
test_read_write_single_tree_named(CuTest * ct)164 void test_read_write_single_tree_named(CuTest* ct) {
165     kdtree_t* kd;
166     double * data;
167     int N = 1000;
168     int Nleaf = 5;
169     int D = 3;
170     char fn[1024];
171     int rtn;
172     kdtree_t* kd2;
173     int fd;
174 
175     data = random_points_d(N, D);
176     kd = build_tree(ct, data, N, D, Nleaf, KDTT_DOUBLE,
177                     KD_BUILD_SPLIT | KD_BUILD_BBOX | KD_BUILD_LINEAR_LR);
178     kd->name = strdup("christmas");
179 
180     sprintf(fn, "/tmp/test_libkd_io_single_tree_named.XXXXXX");
181     fd = mkstemp(fn);
182     if (fd == -1) {
183         fprintf(stderr, "Failed to generate a temp filename: %s\n", strerror(errno));
184         CuFail(ct, "mkstemp");
185     }
186     close(fd);
187     printf("Single tree named: writing to file %s.\n", fn);
188 
189     rtn = kdtree_fits_write(kd, fn, NULL);
190     CuAssertIntEquals(ct, 0, rtn);
191 
192     // Loading any tree should succeed.
193     kd2 = kdtree_fits_read(fn, NULL, NULL);
194     assert_kdtrees_equal(ct, kd, kd2);
195     kdtree_fits_close(kd2);
196 
197     // Attempting to load a nonexist named tree should fail.
198     kd2 = kdtree_fits_read(fn, "none", NULL);
199     CuAssertPtrEquals(ct, NULL, kd2);
200 
201     // Loading by its correct name should work.
202     kd2 = kdtree_fits_read(fn, "christmas", NULL);
203     assert_kdtrees_equal(ct, kd, kd2);
204     kdtree_fits_close(kd2);
205 
206     free(data);
207     kdtree_free(kd);
208 }
209 
test_read_write_two_trees(CuTest * ct)210 void test_read_write_two_trees(CuTest* ct) {
211     kdtree_t* kd;
212     kdtree_t* kdB;
213     double * data;
214     double * dataB;
215     int N = 1000;
216     int Nleaf = 5;
217     int D = 3;
218     char fn[1024];
219     int rtn;
220     kdtree_t* kd2;
221     kdtree_t* kd2B;
222     int fd;
223     kdtree_fits_t* io;
224 
225     data = random_points_d(N, D);
226     kd = build_tree(ct, data, N, D, Nleaf, KDTT_DOUBLE,
227                     KD_BUILD_SPLIT | KD_BUILD_BBOX | KD_BUILD_LINEAR_LR);
228     kd->name = strdup("christmas");
229 
230     dataB = random_points_d(N, D);
231     kdB = build_tree(ct, dataB, N, D, Nleaf, KDTT_DUU,
232                      KD_BUILD_SPLIT | KD_BUILD_SPLITDIM | KD_BUILD_LINEAR_LR);
233     kdB->name = strdup("watermelon");
234 
235     sprintf(fn, "/tmp/test_libkd_io_two_trees.XXXXXX");
236     fd = mkstemp(fn);
237     if (fd == -1) {
238         fprintf(stderr, "Failed to generate a temp filename: %s\n", strerror(errno));
239         CuFail(ct, "mkstemp");
240     }
241     printf("Two trees: writing to file %s.\n", fn);
242 
243     close(fd);
244 
245     io = kdtree_fits_open_for_writing(fn);
246     if (!io) {
247         fprintf(stderr, "Failed to open temp file: %s\n", strerror(errno));
248         CuFail(ct, "fdopen");
249     }
250     rtn = kdtree_fits_write_primary_header(io, NULL);
251     CuAssertIntEquals(ct, 0, rtn);
252     rtn = kdtree_fits_append_tree(io, kd, NULL);
253     CuAssertIntEquals(ct, 0, rtn);
254     rtn = kdtree_fits_append_tree(io, kdB, NULL);
255     CuAssertIntEquals(ct, 0, rtn);
256 
257     if (kdtree_fits_io_close(io)) {
258         fprintf(stderr, "Failed to close temp file: %s\n", strerror(errno));
259         CuFail(ct, "fclose");
260     }
261 
262     // Loading any tree should return the first one.
263     kd2 = kdtree_fits_read(fn, NULL, NULL);
264     assert_kdtrees_equal(ct, kd, kd2);
265     kdtree_fits_close(kd2);
266 
267     // Attempting to load a nonexist named tree should fail.
268     kd2 = kdtree_fits_read(fn, "none", NULL);
269     CuAssertPtrEquals(ct, NULL, kd2);
270 
271     // Loading by the correct names should work.
272     kd2 = kdtree_fits_read(fn, "christmas", NULL);
273     assert_kdtrees_equal(ct, kd, kd2);
274 
275     kd2B = kdtree_fits_read(fn, "watermelon", NULL);
276     assert_kdtrees_equal(ct, kdB, kd2B);
277 
278     kdtree_fits_close(kd2);
279     kdtree_fits_close(kd2B);
280 
281     free(data);
282     kdtree_free(kd);
283 
284     free(dataB);
285     kdtree_free(kdB);
286 }
287 
288