moore_hilbert/lib.rs
1use std::cmp::Ordering;
2use std::convert::TryInto;
3use std::mem;
4
5#[cfg(test)]
6mod tests {
7
8 use crate::*;
9
10 #[test]
11 fn test_increment_coordinates_beyond_bit_range() {
12 // Test that the increment doesn't increment out of bounds
13 let mut loop_coords = vec![0, 0];
14 for _incr_count in 0..1024 {
15 coordinates_increment(4, &mut loop_coords);
16 assert!(loop_coords[0] < 16, "x = {} < 16", loop_coords[0]);
17 assert!(loop_coords[1] < 16, "y = {} < 16", loop_coords[1]);
18 }
19 }
20
21 #[test]
22 fn test_iterate_dimensions() {
23 // 2 dimensions each using 4 bits
24 for x in 0..1 << 4 {
25 for y in 0..1 << 4 {
26 let coords = vec![x, y];
27 let _index = coordinates_to_index(4, &coords);
28 }
29 }
30 }
31}
32
33/// Represent an index on the Hilbert curve
34pub type HilbertIndex = moore_hilbert_sys::BitmaskT;
35
36/// Storage type for a single coordinate
37pub type HilbertCoordinate = u64;
38
39/// The datatype that contain the number of bits per dimension
40pub type BitsPerDimensionType = usize;
41
42/// The number of bits in each byte
43const BITS_PER_BYTE: usize = 8;
44
45/// Convert coordinates of a point on a Hilbert curve to its index
46///
47/// # Arguments
48///
49/// * `bits_per_dimension` - Number of bits/coordinate.
50/// * `coords` - Slice of coordinate values
51///
52/// # Returns
53///
54/// * `index` - Output index value. nDims*nBits bits.
55///
56/// # Assumptions
57///
58/// `length of coords` * `bits_per_dimension` <= (sizeof `HilbertIndex`) * (`bits_per_byte`)
59///
60/// # Example
61///
62/// ```
63/// let bits_per_dimension = 8;
64/// let r = moore_hilbert::coordinates_to_index(bits_per_dimension, &vec![1,2,3]).unwrap();
65/// assert_eq!(r, 36);
66/// ```
67pub fn coordinates_to_index(
68 bits_per_dimension: BitsPerDimensionType,
69 coords: &[HilbertCoordinate],
70) -> Result<HilbertIndex, ()> {
71 if bits_per_dimension * coords.len() > mem::size_of::<HilbertIndex>() * BITS_PER_BYTE {
72 panic!("number of coordinates * bits_per_dimension > sizeof(HilbertIndex) * BITS_PER_BYTE");
73 }
74
75 unsafe {
76 return Ok(moore_hilbert_sys::hilbert_c2i(
77 coords.len().try_into().unwrap(),
78 bits_per_dimension.try_into().unwrap(),
79 coords.as_ptr(),
80 ));
81 }
82}
83
84/// Convert an index into a Hilbert curve to a set of coordinates
85///
86/// # Arguments
87///
88/// * `bits_per_dimension` - Number of bits per dimension
89/// * `index` - The index, contains the number of dimensions * `nBits` bits (so `nDims` * `nBits` must be <= `BITS_PER_BYTE` * sizeof(`HilbertIndex`)).
90/// * `coords` - The slice where the coordinates will be written
91///
92/// # Assumptions
93///
94/// `number of dimesions` * `bits_per_dimension` <= (sizeof `HilbertIndex`) * (`bits_per_byte`)
95///
96/// # Example
97///
98/// ```
99/// let bits_per_dimension = 8;
100///
101/// // Start by getting an index along the Hilbert curve for this point
102/// let start_vec = vec![1,2,3];
103/// let r = moore_hilbert::coordinates_to_index(bits_per_dimension, &start_vec).unwrap();
104/// assert_eq!(r, 36);
105///
106/// // A place to put the coordinates from the Hilbert curve index
107/// let mut extracted_coords = [0,0,0];
108/// moore_hilbert::index_to_coordinates(bits_per_dimension, r, &mut extracted_coords);
109///
110/// /// The coordinates should match.
111/// assert_eq!(start_vec, extracted_coords);
112///
113/// // increment the index and make sure the coords don't match
114/// moore_hilbert::index_to_coordinates(bits_per_dimension, r+1, &mut extracted_coords);
115/// assert_ne!(start_vec, extracted_coords);
116///
117/// ```
118pub fn index_to_coordinates(
119 bits_per_dimension: BitsPerDimensionType,
120 index: HilbertIndex,
121 coords: &mut [HilbertCoordinate],
122) -> () {
123 if bits_per_dimension as usize * coords.len() > mem::size_of::<HilbertIndex>() * BITS_PER_BYTE {
124 panic!("number of coordinates * bits_per_dimension > mem::size_of(HilbertIndex) * BITS_PER_BYTE");
125 }
126
127 unsafe {
128 return moore_hilbert_sys::hilbert_i2c(
129 coords.len().try_into().unwrap(),
130 bits_per_dimension.try_into().unwrap(),
131 index,
132 coords.as_mut_ptr(),
133 );
134 }
135}
136
137/// Determine which of two points lies further along the Hilbert curve
138///
139/// # Arguments
140///
141/// * `bits_per_dimension` - Number of bits/coordinate.
142/// * `coord1` - Slice of coordinates
143/// * `coord2` - Slice of coordinates
144///
145/// # Returns
146///
147/// * Ordering result that indicates the comparison of coord1 and coord2
148///
149/// # Assumptions
150///
151/// `nBits` <= (sizeof `HilbertIndex`) * `bits_per_byte`
152///
153/// # Example
154///
155/// ```
156/// use std::cmp::Ordering;
157/// let coords = vec![
158/// vec![1,2,3],
159/// vec![1,2,4],
160/// ];
161///
162/// let bits_per_dimension = 4;
163///
164/// assert_eq!(moore_hilbert::coordinates_compare(bits_per_dimension, &coords[0], &coords[1]), Ordering::Less);
165/// assert_eq!(moore_hilbert::coordinates_compare(bits_per_dimension, &coords[0], &coords[0]), Ordering::Equal);
166/// assert_eq!(moore_hilbert::coordinates_compare(bits_per_dimension, &coords[1], &coords[0]), Ordering::Greater);
167/// ```
168pub fn coordinates_compare(
169 bits_per_dimension: BitsPerDimensionType,
170 coord1: &[HilbertCoordinate],
171 coord2: &[HilbertCoordinate],
172) -> Ordering {
173 if bits_per_dimension as usize > mem::size_of::<HilbertIndex>() * BITS_PER_BYTE {
174 panic!("bits_per_dimension > mem::size_of::<HilbertIndex>() * BITS_PER_BYTE");
175 }
176
177 if coord1.len() != coord2.len() {
178 panic!("Coordinates supplied are not equal in length");
179 }
180
181 unsafe {
182 let r = moore_hilbert_sys::hilbert_cmp(
183 coord1.len().try_into().unwrap(),
184 mem::size_of::<HilbertCoordinate>().try_into().unwrap(),
185 bits_per_dimension.try_into().unwrap(),
186 coord1.as_ptr() as *const std::ffi::c_void,
187 coord2.as_ptr() as *const std::ffi::c_void,
188 );
189 if r == -1 {
190 return Ordering::Less;
191 }
192 if r == 0 {
193 return Ordering::Equal;
194 }
195 return Ordering::Greater;
196 }
197}
198
199/// Determine which of two points lies further along the Hilbert curve
200///
201/// # Arguments
202///
203/// * `coord1` - Slice of coordinates using floats
204/// * `coord2` - Slice of coordinates using floats
205///
206/// # Returns
207///
208/// * Ordering result that indicates the comparison of coord1 and coord2
209///
210/// ```
211/// use std::cmp::Ordering;
212/// let coords = vec![
213/// vec![1.0, 2.0, 3.0],
214/// vec![1.0, 2.0, 4.0],
215/// ];
216///
217///
218/// assert_eq!(moore_hilbert::coordinates_float_compare(&coords[0], &coords[0]), Ordering::Equal);
219/// assert_eq!(moore_hilbert::coordinates_float_compare(&coords[0], &coords[1]), Ordering::Greater);
220/// assert_eq!(moore_hilbert::coordinates_float_compare(&coords[1], &coords[0]), Ordering::Less);
221/// ```
222pub fn coordinates_float_compare(coord1: &[f64], coord2: &[f64]) -> Ordering {
223 if coord1.len() != coord2.len() {
224 panic!("Coordinates supplied are not equal in length");
225 }
226
227 unsafe {
228 let r = moore_hilbert_sys::hilbert_ieee_cmp(
229 coord1.len().try_into().unwrap(),
230 coord1.as_ptr(),
231 coord2.as_ptr(),
232 );
233 if r == -1 {
234 return Ordering::Less;
235 }
236 if r == 0 {
237 return Ordering::Equal;
238 }
239 return Ordering::Greater;
240 }
241}
242
243/// Advance from one point to its successor on a Hilbert curve
244///
245/// # Arguments
246///
247/// * `bits_per_dimension` - Number of bits/coordinate.
248/// * `coord` - Coordinates that will be modified to be the next point on the curve
249///
250/// # Assumptions
251///
252/// `bits_per_dimension` <= (sizeof `HilbertIndex`) * (`bits_per_byte`)
253///
254/// # Example
255///
256/// ```
257/// let bits_per_dimension = 8;
258/// let mut coords = vec![2, 2, 5];
259///
260/// /// Get the initial position along the Hilbert curve
261/// let first_index = moore_hilbert::coordinates_to_index(bits_per_dimension, &coords).unwrap();
262///
263/// /// Increment that position
264/// moore_hilbert::coordinates_increment(bits_per_dimension, &mut coords);
265///
266/// /// Convert the incremented position back to a new index on the Hilbert curve
267/// let new_index = moore_hilbert::coordinates_to_index(bits_per_dimension, &coords).unwrap();
268///
269/// /// The newly incremented index should advance along the curve by 1.
270/// assert_eq!(new_index-first_index, 1);
271///
272/// ```
273pub fn coordinates_increment(
274 bits_per_dimension: BitsPerDimensionType,
275 coord: &mut [HilbertCoordinate],
276) -> () {
277 if bits_per_dimension as usize > mem::size_of::<HilbertIndex>() * BITS_PER_BYTE {
278 panic!("bits_per_dimension > mem::size_of::<HilbertIndex>() * BITS_PER_BYTE");
279 }
280 unsafe {
281 moore_hilbert_sys::hilbert_incr(
282 coord.len().try_into().unwrap(),
283 bits_per_dimension.try_into().unwrap(),
284 coord.as_mut_ptr(),
285 );
286 }
287}
288
289/// Determine the first or last vertex of a box to lie on a Hilbert curve
290///
291/// # Arguments
292///
293/// * `bits_per_dimension` - Number of bits per coordinate
294/// * `find_min` - Is the least vertex sought?
295/// * `coord1` - One corner of box
296/// * `coord2` - Opposite corner
297///
298/// # Returns
299///
300/// `coord1` and `coord2` modified to refer to selected corner
301/// value returned is log2 of size of largest power-of-two-aligned box that
302/// contains the selected corner and no other corners
303///
304/// # Assumptions
305///
306/// `bits_per_dimension` <= (sizeof `HilbertIndex`) * (`bits_per_byte`)
307///
308pub fn box_vertex(
309 bits_per_dimension: BitsPerDimensionType,
310 find_min: bool,
311 coord1: &mut [HilbertCoordinate],
312 coord2: &mut [HilbertCoordinate],
313) -> usize {
314 if coord1.len() != coord2.len() {
315 panic!("Coordinates supplied are not equal in length");
316 }
317
318 if bits_per_dimension as usize > mem::size_of::<HilbertIndex>() * BITS_PER_BYTE {
319 panic!("bits_per_dimension > mem::size_of::<HilbertIndex>() * BITS_PER_BYTE");
320 }
321
322 unsafe {
323 return moore_hilbert_sys::hilbert_box_vtx(
324 coord1.len().try_into().unwrap(),
325 mem::size_of::<HilbertCoordinate>().try_into().unwrap(),
326 bits_per_dimension.try_into().unwrap(),
327 if find_min { 1 } else { 0 },
328 coord1.as_ptr() as *mut std::ffi::c_void,
329 coord2.as_ptr() as *mut std::ffi::c_void,
330 ) as usize;
331 }
332}
333
334/// Determine the first or last vertex of a box to lie on a Hilbert curve
335///
336/// # Arguments
337///
338/// * `find_min` - Is the least vertex sought?
339/// * `c1` - One corner of box
340/// * `c2` - Opposite corner
341///
342/// # Returns
343/// `c1` and `c2` modified to refer to selected corder
344/// value returned is log2 of size of largest power-of-two-aligned box that
345/// contains the selected corner and no other corners
346///
347/// # Assumptions
348///
349/// `bits_per_dimension` <= (sizeof `HilbertIndex`) * (`bits_per_byte`)
350///
351pub fn box_float_vertex(find_min: bool, coord1: &mut [f64], coord2: &mut [f64]) -> usize {
352 if coord1.len() != coord2.len() {
353 panic!("Coordinates supplied are not equal in length");
354 }
355
356 unsafe {
357 return moore_hilbert_sys::hilbert_ieee_box_vtx(
358 coord1.len().try_into().unwrap(),
359 if find_min { 1 } else { 0 },
360 coord1.as_mut_ptr(),
361 coord2.as_mut_ptr(),
362 ) as usize;
363 }
364}
365
366/// Determine the first or last point of a box to lie on a Hilbert curve
367///
368/// # Arguments
369///
370/// * `bits_per_dimension` - Number of bits/coordinate.
371/// * `find_min` - Is it the least vertex sought?
372/// * `coord1` - Coordinates of one corner of box
373/// * `coord2` - Coordinates of the opposite corner of box
374///
375/// # Returns
376///
377/// `coord1` and `coord2` are modified to refer to the least point
378///
379/// # Assumptions
380///
381/// `bits_per_dimension` <= (sizeof `HilbertIndex`) * (`bits_per_byte`)
382///
383/// # Example
384///
385/// ```
386/// let bits_per_dimension = 8;
387/// let starting_corners = vec![
388/// vec![0, 0], // smallest coordinate point
389/// vec![10, 10] // largest coordinate point
390/// ];
391///
392/// // Since the coordinates will be overwritten when finding the points of the box
393/// // make copies.
394///
395/// let mut low_corner_1 = starting_corners[0].clone();
396/// let mut high_corner_1 = starting_corners[1].clone();
397/// moore_hilbert::box_point(bits_per_dimension, true, &mut low_corner_1, &mut high_corner_1);
398///
399/// // Both points should equal each other after the function call
400/// assert_eq!(high_corner_1, low_corner_1);
401///
402/// let low_point = low_corner_1.clone();
403///
404/// // Now get the high point.
405/// low_corner_1 = starting_corners[0].clone();
406/// high_corner_1 = starting_corners[1].clone();
407/// moore_hilbert::box_point(bits_per_dimension, false, &mut low_corner_1, &mut high_corner_1);
408///
409/// // Both points should equal each other after the function call
410/// assert_eq!(high_corner_1, low_corner_1);
411///
412/// let high_point = low_corner_1.clone();
413///
414/// assert_eq!(low_point, vec![0, 0]);
415/// assert_eq!(high_point, vec![1, 10]);
416///
417/// ```
418pub fn box_point(
419 bits_per_dimension: BitsPerDimensionType,
420 find_min: bool,
421 coord1: &mut [HilbertCoordinate],
422 coord2: &mut [HilbertCoordinate],
423) -> usize {
424 if coord1.len() != coord2.len() {
425 panic!("Coordinates supplied are not equal in length");
426 }
427
428 unsafe {
429 return moore_hilbert_sys::hilbert_box_pt(
430 coord1.len().try_into().unwrap(),
431 mem::size_of::<HilbertCoordinate>().try_into().unwrap(),
432 bits_per_dimension.try_into().unwrap(),
433 if find_min { 1 } else { 0 },
434 coord1.as_mut_ptr() as *mut std::ffi::c_void,
435 coord2.as_mut_ptr() as *mut std::ffi::c_void,
436 ) as usize;
437 }
438}
439
440/// Determine the first or last point of a box to lie on a Hilbert curve
441///
442/// # Arguments
443///
444/// * `bits_per_dimension` - Number of bits/coordinate.
445/// * `find_min` - Is it the least vertex sought?
446/// * `coord1` - Coordinates of one corner of box
447/// * `coord2` - Coordinates of the opposite corner of box
448///
449/// # Returns
450///
451/// `coord1` and `coord2` are modified to refer to the least point
452///
453/// # Assumptions
454///
455/// `bits_per_dimension` <= (sizeof `HilbertIndex`) * (`bits_per_byte`)
456///
457pub fn box_point_float(find_min: bool, coord1: &mut [f64], coord2: &mut [f64]) -> usize {
458 if coord1.len() != coord2.len() {
459 panic!("Coordinates supplied are not equal in length");
460 }
461
462 unsafe {
463 return moore_hilbert_sys::hilbert_ieee_box_pt(
464 coord1.len().try_into().unwrap(),
465 if find_min { 1 } else { 0 },
466 coord1.as_mut_ptr(),
467 coord2.as_mut_ptr(),
468 ) as usize;
469 }
470}
471
472/// Determine the first point of a box after a given point to lie on a Hilbert curve.
473///
474/// # Arguments
475///
476/// * `bits_per_dimension` - Number of bits per dimension
477/// * `find_prev` - Is the previous point sought?
478/// * `coord1` - Coordinates of one corner of the box
479/// * `coord2` - Coordinates of the opposite corner of the box
480/// * `point` - Coordinates which are a lower bound on the point returned
481///
482/// # Returns
483///
484/// If true `coord1` and `coord2` are modified to point to the least point after `point` in the box.
485///
486/// If false the arguments are unchanged and the point is beyond the last point of the box
487///
488/// # Assumptions
489///
490/// `bits_per_dimension` <= (sizeof `HilbertIndex`) * (`bits_per_byte`)
491///
492pub fn box_next_point(
493 bits_per_dimension: BitsPerDimensionType,
494 find_prev: bool,
495 coord1: &mut [HilbertCoordinate],
496 coord2: &mut [HilbertCoordinate],
497 point: &[HilbertCoordinate],
498) -> bool {
499 if coord1.len() != coord2.len() {
500 panic!("Coordinates supplied are not equal in length");
501 }
502
503 if point.len() != coord1.len() {
504 panic!("Coordinates supplied are not equal in length to the point supplied");
505 }
506
507 unsafe {
508 if moore_hilbert_sys::hilbert_nextinbox(
509 coord1.len().try_into().unwrap(),
510 mem::size_of::<HilbertCoordinate>().try_into().unwrap(),
511 bits_per_dimension.try_into().unwrap(),
512 if find_prev { 1 } else { 0 },
513 coord1.as_mut_ptr() as *mut std::ffi::c_void,
514 coord2.as_mut_ptr() as *mut std::ffi::c_void,
515 point.as_ptr() as *mut std::ffi::c_void,
516 ) == 1
517 {
518 true
519 } else {
520 false
521 }
522 }
523}