1 /*
2 libparted - a library for manipulating disk partitions
3 Copyright (C) 2000, 2007 Free Software Foundation, Inc.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /**
20 * \file natmath.c
21 */
22
23 /**
24 * \addtogroup PedAlignment
25 *
26 * \brief Alignment constraint model.
27 *
28 * This part of libparted models alignment constraints.
29 *
30 * @{
31 */
32
33 #include <config.h>
34 #include <stdlib.h>
35 #include <parted/parted.h>
36 #include <parted/debug.h>
37 #include <parted/natmath.h>
38
39 /* Arrrghhh! Why doesn't C have tuples? */
40 typedef struct {
41 PedSector gcd; /* "converges" to the gcd */
42 PedSector x;
43 PedSector y;
44 } EuclidTriple;
45
46 static const PedAlignment _any = {
47 .offset = 0,
48 .grain_size = 1
49 };
50
51 const PedAlignment* ped_alignment_any = &_any;
52 const PedAlignment* ped_alignment_none = NULL;
53
54 /* This function returns "a mod b", the way C should have done it!
55 * Mathematicians prefer -3 mod 4 to be 3. Reason: division by N
56 * is all about adding or subtracting N, and we like our remainders
57 * to be between 0 and N - 1.
58 */
59 PedSector
abs_mod(PedSector a,PedSector b)60 abs_mod (PedSector a, PedSector b)
61 {
62 if (a < 0)
63 return a % b + b;
64 else
65 return a % b;
66 }
67
68 /* Rounds a number down to the closest number that is a multiple of
69 * grain_size.
70 */
71 PedSector
ped_round_down_to(PedSector sector,PedSector grain_size)72 ped_round_down_to (PedSector sector, PedSector grain_size)
73 {
74 return sector - abs_mod (sector, grain_size);
75 }
76
77 #ifdef __sun
78 extern PedSector
79 #else
80 extern inline PedSector
81 #endif
ped_div_round_up(PedSector numerator,PedSector divisor)82 ped_div_round_up (PedSector numerator, PedSector divisor)
83 {
84 return (numerator + divisor - 1) / divisor;
85 }
86
87 #ifdef __sun
88 extern PedSector
89 #else
90 extern inline PedSector
91 #endif
ped_div_round_to_nearest(PedSector numerator,PedSector divisor)92 ped_div_round_to_nearest (PedSector numerator, PedSector divisor)
93 {
94 return (numerator + divisor/2) / divisor;
95 }
96
97 /* Rounds a number up to the closest number that is a multiple of
98 * grain_size.
99 */
100 PedSector
ped_round_up_to(PedSector sector,PedSector grain_size)101 ped_round_up_to (PedSector sector, PedSector grain_size)
102 {
103 if (sector % grain_size)
104 return ped_round_down_to (sector, grain_size) + grain_size;
105 else
106 return sector;
107 }
108
109 /* Rounds a number to the closest number that is a multiple of grain_size. */
110 PedSector
ped_round_to_nearest(PedSector sector,PedSector grain_size)111 ped_round_to_nearest (PedSector sector, PedSector grain_size)
112 {
113 if (sector % grain_size > grain_size/2)
114 return ped_round_up_to (sector, grain_size);
115 else
116 return ped_round_down_to (sector, grain_size);
117 }
118
119 /* This function returns the largest number that divides both a and b.
120 * It uses the ancient Euclidean algorithm.
121 */
122 PedSector
ped_greatest_common_divisor(PedSector a,PedSector b)123 ped_greatest_common_divisor (PedSector a, PedSector b)
124 {
125 PED_ASSERT (a >= 0, return 0);
126 PED_ASSERT (b >= 0, return 0);
127
128 /* Put the arguments in the "right" format. (Recursive calls made by
129 * this function are always in the right format.)
130 */
131 if (b > a)
132 return ped_greatest_common_divisor (b, a);
133
134 if (b)
135 return ped_greatest_common_divisor (b, a % b);
136 else
137 return a;
138 }
139
140 /**
141 * Initialize a preallocated piece of memory for an alignment object
142 * (used by PedConstraint).
143 *
144 * The object will represent all sectors \e s for which the equation
145 * <tt>s = offset + X * grain_size</tt> holds.
146 */
147 int
ped_alignment_init(PedAlignment * align,PedSector offset,PedSector grain_size)148 ped_alignment_init (PedAlignment* align, PedSector offset, PedSector grain_size)
149 {
150 PED_ASSERT (align != NULL, return 0);
151
152 if (grain_size < 0)
153 return 0;
154
155 if (grain_size)
156 align->offset = abs_mod (offset, grain_size);
157 else
158 align->offset = offset;
159 align->grain_size = grain_size;
160
161 return 1;
162 }
163
164 /**
165 * Return an alignment object (used by PedConstraint), representing all
166 * PedSector's that are of the form <tt>offset + X * grain_size</tt>.
167 */
168 PedAlignment*
ped_alignment_new(PedSector offset,PedSector grain_size)169 ped_alignment_new (PedSector offset, PedSector grain_size)
170 {
171 PedAlignment* align;
172
173 align = (PedAlignment*) ped_malloc (sizeof (PedAlignment));
174 if (!align)
175 goto error;
176
177 if (!ped_alignment_init (align, offset, grain_size))
178 goto error_free_align;
179
180 return align;
181
182 error_free_align:
183 ped_free (align);
184 error:
185 return NULL;
186 }
187
188 /**
189 * Free up memory associated with \p align.
190 */
191 void
ped_alignment_destroy(PedAlignment * align)192 ped_alignment_destroy (PedAlignment* align)
193 {
194 if (align)
195 ped_free (align);
196 }
197
198 /**
199 * Return a duplicate of \p align.
200 */
201 PedAlignment*
ped_alignment_duplicate(const PedAlignment * align)202 ped_alignment_duplicate (const PedAlignment* align)
203 {
204 if (!align)
205 return NULL;
206 return ped_alignment_new (align->offset, align->grain_size);
207 }
208
209 /* the extended Euclid algorithm.
210 *
211 * input:
212 * a and b, a > b
213 *
214 * output:
215 * gcd, x and y, such that:
216 *
217 * gcd = greatest common divisor of a and b
218 * gcd = x*a + y*b
219 */
220 EuclidTriple
extended_euclid(int a,int b)221 extended_euclid (int a, int b)
222 {
223 EuclidTriple result;
224 EuclidTriple tmp;
225
226 if (b == 0) {
227 result.gcd = a;
228 result.x = 1;
229 result.y = 0;
230 return result;
231 }
232
233 tmp = extended_euclid (b, a % b);
234 result.gcd = tmp.gcd;
235 result.x = tmp.y;
236 result.y = tmp.x - (a/b) * tmp.y;
237 return result;
238 }
239
240 /**
241 * This function computes a PedAlignment object that describes the
242 * intersection of two alignments. That is, a sector satisfies the
243 * new alignment object if and only if it satisfies both of the original
244 * ones. (See ped_alignment_is_aligned() for the meaning of "satisfies")
245 *
246 * Apart from the trivial cases (where one or both of the alignment objects
247 * constraints have no sectors that satisfy them), this is what we're trying to
248 * do:
249 * - two input constraints: \p a and \p b.
250 * - the new grain_size is going to be the lowest common multiple of
251 * \p a->grain_size and \p b->grain_size
252 * - hard part - solve the simultaneous equations, for offset, where offset,
253 * X and Y are variables. (Note: offset can be obtained from either X or Y,
254 * by substituing into either equation)
255 *
256 * \code
257 * offset = \p a->offset + X * \p a->grain_size (1)
258 * offset = \p b->offset + Y * \p b->grain_size (2)
259 * \endcode
260 *
261 * or, abbreviated:
262 *
263 * \code
264 * o = Ao + X*Ag (1)
265 * o = Bo + Y*Bg (2)
266 *
267 * => Ao + X*Ag = Bo + Y*Bg (1) = (2)
268 * X*Ag - Y*Bg = Bo - Ao (3)
269 * \endcode
270 *
271 * As it turns out, there only exists a solution if (Bo - Ao) is a multiple
272 * of the GCD of Ag and Bg. Reason: all linear combinations of Ag and Bg are
273 * multiples of the GCD.
274 *
275 * Proof:
276 *
277 * \code
278 * A * Ag + B * Bg
279 * = A * (\p a * gcd) + B * (\p b * gcd)
280 * = gcd * (A * \p a + B * \p b)
281 * \endcode
282 *
283 * gcd is a factor of the linear combination. QED
284 *
285 * Anyway, \p a * Ag + \p b * Bg = gcd can be solved (for \p a, \p b and gcd)
286 * with Euclid's extended algorithm. Then, we just multiply through by
287 * (Bo - Ao) / gcd to get (3).
288 *
289 * i.e.
290 * \code
291 * A * Ag + B * Bg = gcd
292 * A*(Bo-Ao)/gcd * Ag + B(Bo-Ao)/gcd * Bg = gcd * (Bo-Ao)/gcd
293 * X*Ag - Y*Bg = Bo - Ao (3)
294 *
295 * X = A*(Bo-Ao)/gcd
296 * Y = - B*(Bo-Ao)/gcd
297 * \endcode
298 *
299 * then:
300 * \code
301 * o = Ao + X*Ag (1)
302 * = Ao + A*(Bo-Ao)/gcd*Ag
303 * o = Bo + Y*Bg (2)
304 * = Bo - B*(Bo-Ao)/gcd*Ag
305 * \endcode
306 *
307 * Thanks go to Nathan Hurst (njh@hawthorn.csse.monash.edu.au) for figuring
308 * this algorithm out :-)
309 *
310 * \note Returned \c NULL is a valid PedAlignment object, and can be used
311 for ped_alignment_*() function.
312 *
313 * \return a PedAlignment on success, \c NULL on failure
314 */
315 PedAlignment*
ped_alignment_intersect(const PedAlignment * a,const PedAlignment * b)316 ped_alignment_intersect (const PedAlignment* a, const PedAlignment* b)
317 {
318 PedSector new_grain_size;
319 PedSector new_offset;
320 PedSector delta_on_gcd;
321 EuclidTriple gcd_factors;
322
323
324 if (!a || !b)
325 return NULL;
326
327 /*PED_DEBUG (0x10, "intersecting alignments (%d,%d) and (%d,%d)",
328 a->offset, a->grain_size, b->offset, b->grain_size);
329 */
330
331 if (a->grain_size < b->grain_size) {
332 const PedAlignment* tmp;
333 tmp = a; a = b; b = tmp;
334 }
335
336 /* weird/trivial case: where the solution space for "a" or "b" is
337 * either empty or contains exactly one solution
338 */
339 if (a->grain_size == 0 && b->grain_size == 0) {
340 if (a->offset == b->offset)
341 return ped_alignment_duplicate (a);
342 else
343 return NULL;
344 }
345
346 /* general case */
347 gcd_factors = extended_euclid (a->grain_size, b->grain_size);
348
349 delta_on_gcd = (b->offset - a->offset) / gcd_factors.gcd;
350 new_offset = a->offset + gcd_factors.x * delta_on_gcd * a->grain_size;
351 new_grain_size = a->grain_size * b->grain_size / gcd_factors.gcd;
352
353 /* inconsistency => no solution */
354 if (new_offset
355 != b->offset - gcd_factors.y * delta_on_gcd * b->grain_size)
356 return NULL;
357
358 return ped_alignment_new (new_offset, new_grain_size);
359 }
360
361 /* This function returns the sector closest to "sector" that lies inside
362 * geom and satisfies the alignment constraint.
363 */
364 static PedSector
_closest_inside_geometry(const PedAlignment * align,const PedGeometry * geom,PedSector sector)365 _closest_inside_geometry (const PedAlignment* align, const PedGeometry* geom,
366 PedSector sector)
367 {
368 PED_ASSERT (align != NULL, return -1);
369
370 if (!align->grain_size) {
371 if (ped_alignment_is_aligned (align, geom, sector)
372 && (!geom || ped_geometry_test_sector_inside (geom,
373 sector)))
374 return sector;
375 else
376 return -1;
377 }
378
379 if (sector < geom->start)
380 sector += ped_round_up_to (geom->start - sector,
381 align->grain_size);
382 if (sector > geom->end)
383 sector -= ped_round_up_to (sector - geom->end,
384 align->grain_size);
385
386 if (!ped_geometry_test_sector_inside (geom, sector))
387 return -1;
388 return sector;
389 }
390
391 /**
392 * This function returns the closest sector to \p sector that lies inside
393 * \p geom that satisfies the given alignment constraint \p align. It prefers
394 * sectors that are beyond \p sector (are not smaller than \p sector),
395 * but does not guarantee that this.
396 *
397 * \return a PedSector on success, \c -1 on failure
398 */
399 PedSector
ped_alignment_align_up(const PedAlignment * align,const PedGeometry * geom,PedSector sector)400 ped_alignment_align_up (const PedAlignment* align, const PedGeometry* geom,
401 PedSector sector)
402 {
403 PedSector result;
404
405 PED_ASSERT (align != NULL, return -1);
406
407 if (!align->grain_size)
408 result = align->offset;
409 else
410 result = ped_round_up_to (sector - align->offset,
411 align->grain_size)
412 + align->offset;
413
414 if (geom)
415 result = _closest_inside_geometry (align, geom, result);
416 return result;
417 }
418
419 /**
420 * This function returns the closest sector to \p sector that lies inside
421 * \p geom that satisfies the given alignment constraint \p align. It prefers
422 * sectors that are before \p sector (are not larger than \p sector),
423 * but does not guarantee that this.
424 *
425 * \return a PedSector on success, \c -1 on failure
426 */
427 PedSector
ped_alignment_align_down(const PedAlignment * align,const PedGeometry * geom,PedSector sector)428 ped_alignment_align_down (const PedAlignment* align, const PedGeometry* geom,
429 PedSector sector)
430 {
431 PedSector result;
432
433 PED_ASSERT (align != NULL, return -1);
434
435 if (!align->grain_size)
436 result = align->offset;
437 else
438 result = ped_round_down_to (sector - align->offset,
439 align->grain_size)
440 + align->offset;
441
442 if (geom)
443 result = _closest_inside_geometry (align, geom, result);
444 return result;
445 }
446
447 /* Returns either a or b, depending on which is closest to "sector". */
448 static PedSector
closest(PedSector sector,PedSector a,PedSector b)449 closest (PedSector sector, PedSector a, PedSector b)
450 {
451 if (a == -1)
452 return b;
453 if (b == -1)
454 return a;
455
456 if (abs (sector - a) < abs (sector - b))
457 return a;
458 else
459 return b;
460 }
461
462 /**
463 * This function returns the sector that is closest to \p sector,
464 * satisfies the \p align constraint and lies inside \p geom.
465 *
466 * \return a PedSector on success, \c -1 on failure
467 */
468 PedSector
ped_alignment_align_nearest(const PedAlignment * align,const PedGeometry * geom,PedSector sector)469 ped_alignment_align_nearest (const PedAlignment* align, const PedGeometry* geom,
470 PedSector sector)
471 {
472 PED_ASSERT (align != NULL, return -1);
473
474 return closest (sector, ped_alignment_align_up (align, geom, sector),
475 ped_alignment_align_down (align, geom, sector));
476 }
477
478 /**
479 * This function returns 1 if \p sector satisfies the alignment
480 * constraint \p align and lies inside \p geom.
481 *
482 * \return \c 1 on success, \c 0 on failure
483 */
484 int
ped_alignment_is_aligned(const PedAlignment * align,const PedGeometry * geom,PedSector sector)485 ped_alignment_is_aligned (const PedAlignment* align, const PedGeometry* geom,
486 PedSector sector)
487 {
488 if (!align)
489 return 0;
490
491 if (geom && !ped_geometry_test_sector_inside (geom, sector))
492 return 0;
493
494 if (align->grain_size)
495 return (sector - align->offset) % align->grain_size == 0;
496 else
497 return sector == align->offset;
498 }
499
500 /**
501 * @}
502 */
503
504