blob: f725cbbf3462ca903ecae592f8fd73ac308f1aec [file] [log] [blame]
/*
libparted - a library for manipulating disk partitions
Copyright (C) 2000, 2007 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
/**
* \file natmath.c
*/
/**
* \addtogroup PedAlignment
*
* \brief Alignment constraint model.
*
* This part of libparted models alignment constraints.
*
* @{
*/
#include <config.h>
#include <stdlib.h>
#include <parted/parted.h>
#include <parted/debug.h>
#include <parted/natmath.h>
/* Arrrghhh! Why doesn't C have tuples? */
typedef struct {
PedSector gcd; /* "converges" to the gcd */
PedSector x;
PedSector y;
} EuclidTriple;
static const PedAlignment _any = {
offset: 0,
grain_size: 1
};
const PedAlignment* ped_alignment_any = &_any;
const PedAlignment* ped_alignment_none = NULL;
/* This function returns "a mod b", the way C should have done it!
* Mathematicians prefer -3 mod 4 to be 3. Reason: division by N
* is all about adding or subtracting N, and we like our remainders
* to be between 0 and N - 1.
*/
PedSector
abs_mod (PedSector a, PedSector b)
{
if (a < 0)
return a % b + b;
else
return a % b;
}
/* Rounds a number down to the closest number that is a multiple of
* grain_size.
*/
PedSector
ped_round_down_to (PedSector sector, PedSector grain_size)
{
return sector - abs_mod (sector, grain_size);
}
inline PedSector
ped_div_round_up (PedSector numerator, PedSector divisor)
{
return (numerator + divisor - 1) / divisor;
}
inline PedSector
ped_div_round_to_nearest (PedSector numerator, PedSector divisor)
{
return (numerator + divisor/2) / divisor;
}
/* Rounds a number up to the closest number that is a multiple of
* grain_size.
*/
PedSector
ped_round_up_to (PedSector sector, PedSector grain_size)
{
if (sector % grain_size)
return ped_round_down_to (sector, grain_size) + grain_size;
else
return sector;
}
/* Rounds a number to the closest number that is a multiple of grain_size. */
PedSector
ped_round_to_nearest (PedSector sector, PedSector grain_size)
{
if (sector % grain_size > grain_size/2)
return ped_round_up_to (sector, grain_size);
else
return ped_round_down_to (sector, grain_size);
}
/* This function returns the largest number that divides both a and b.
* It uses the ancient Euclidean algorithm.
*/
PedSector
ped_greatest_common_divisor (PedSector a, PedSector b)
{
PED_ASSERT (a >= 0, return 0);
PED_ASSERT (b >= 0, return 0);
/* Put the arguments in the "right" format. (Recursive calls made by
* this function are always in the right format.)
*/
if (b > a)
return ped_greatest_common_divisor (b, a);
if (b)
return ped_greatest_common_divisor (b, a % b);
else
return a;
}
/**
* Initialize a preallocated piece of memory for an alignment object
* (used by PedConstraint).
*
* The object will represent all sectors \e s for which the equation
* <tt>s = offset + X * grain_size</tt> holds.
*/
int
ped_alignment_init (PedAlignment* align, PedSector offset, PedSector grain_size)
{
PED_ASSERT (align != NULL, return 0);
if (grain_size < 0)
return 0;
if (grain_size)
align->offset = abs_mod (offset, grain_size);
else
align->offset = offset;
align->grain_size = grain_size;
return 1;
}
/**
* Return an alignment object (used by PedConstraint), representing all
* PedSector's that are of the form <tt>offset + X * grain_size</tt>.
*/
PedAlignment*
ped_alignment_new (PedSector offset, PedSector grain_size)
{
PedAlignment* align;
align = (PedAlignment*) ped_malloc (sizeof (PedAlignment));
if (!align)
goto error;
if (!ped_alignment_init (align, offset, grain_size))
goto error_free_align;
return align;
error_free_align:
ped_free (align);
error:
return NULL;
}
/**
* Free up memory associated with \p align.
*/
void
ped_alignment_destroy (PedAlignment* align)
{
if (align)
ped_free (align);
}
/**
* Return a duplicate of \p align.
*/
PedAlignment*
ped_alignment_duplicate (const PedAlignment* align)
{
if (!align)
return NULL;
return ped_alignment_new (align->offset, align->grain_size);
}
/* the extended Euclid algorithm.
*
* input:
* a and b, a > b
*
* output:
* gcd, x and y, such that:
*
* gcd = greatest common divisor of a and b
* gcd = x*a + y*b
*/
EuclidTriple
extended_euclid (int a, int b)
{
EuclidTriple result;
EuclidTriple tmp;
if (b == 0) {
result.gcd = a;
result.x = 1;
result.y = 0;
return result;
}
tmp = extended_euclid (b, a % b);
result.gcd = tmp.gcd;
result.x = tmp.y;
result.y = tmp.x - (a/b) * tmp.y;
return result;
}
/**
* This function computes a PedAlignment object that describes the
* intersection of two alignments. That is, a sector satisfies the
* new alignment object if and only if it satisfies both of the original
* ones. (See ped_alignment_is_aligned() for the meaning of "satisfies")
*
* Apart from the trivial cases (where one or both of the alignment objects
* constraints have no sectors that satisfy them), this is what we're trying to
* do:
* - two input constraints: \p a and \p b.
* - the new grain_size is going to be the lowest common multiple of
* \p a->grain_size and \p b->grain_size
* - hard part - solve the simultaneous equations, for offset, where offset,
* X and Y are variables. (Note: offset can be obtained from either X or Y,
* by substituing into either equation)
*
* \code
* offset = \p a->offset + X * \p a->grain_size (1)
* offset = \p b->offset + Y * \p b->grain_size (2)
* \endcode
*
* or, abbreviated:
*
* \code
* o = Ao + X*Ag (1)
* o = Bo + Y*Bg (2)
*
* => Ao + X*Ag = Bo + Y*Bg (1) = (2)
* X*Ag - Y*Bg = Bo - Ao (3)
* \endcode
*
* As it turns out, there only exists a solution if (Bo - Ao) is a multiple
* of the GCD of Ag and Bg. Reason: all linear combinations of Ag and Bg are
* multiples of the GCD.
*
* Proof:
*
* \code
* A * Ag + B * Bg
* = A * (\p a * gcd) + B * (\p b * gcd)
* = gcd * (A * \p a + B * \p b)
* \endcode
*
* gcd is a factor of the linear combination. QED
*
* Anyway, \p a * Ag + \p b * Bg = gcd can be solved (for \p a, \p b and gcd)
* with Euclid's extended algorithm. Then, we just multiply through by
* (Bo - Ao) / gcd to get (3).
*
* i.e.
* \code
* A * Ag + B * Bg = gcd
* A*(Bo-Ao)/gcd * Ag + B(Bo-Ao)/gcd * Bg = gcd * (Bo-Ao)/gcd
* X*Ag - Y*Bg = Bo - Ao (3)
*
* X = A*(Bo-Ao)/gcd
* Y = - B*(Bo-Ao)/gcd
* \endcode
*
* then:
* \code
* o = Ao + X*Ag (1)
* = Ao + A*(Bo-Ao)/gcd*Ag
* o = Bo + Y*Bg (2)
* = Bo - B*(Bo-Ao)/gcd*Ag
* \endcode
*
* Thanks go to Nathan Hurst (njh@hawthorn.csse.monash.edu.au) for figuring
* this algorithm out :-)
*
* \note Returned \c NULL is a valid PedAlignment object, and can be used
for ped_alignment_*() function.
*
* \return a PedAlignment on success, \c NULL on failure
*/
PedAlignment*
ped_alignment_intersect (const PedAlignment* a, const PedAlignment* b)
{
PedSector new_grain_size;
PedSector new_offset;
PedSector delta_on_gcd;
EuclidTriple gcd_factors;
if (!a || !b)
return NULL;
/*PED_DEBUG (0x10, "intersecting alignments (%d,%d) and (%d,%d)",
a->offset, a->grain_size, b->offset, b->grain_size);
*/
if (a->grain_size < b->grain_size) {
const PedAlignment* tmp;
tmp = a; a = b; b = tmp;
}
/* weird/trivial case: where the solution space for "a" or "b" is
* either empty or contains exactly one solution
*/
if (a->grain_size == 0 && b->grain_size == 0) {
if (a->offset == b->offset)
return ped_alignment_duplicate (a);
else
return NULL;
}
/* general case */
gcd_factors = extended_euclid (a->grain_size, b->grain_size);
delta_on_gcd = (b->offset - a->offset) / gcd_factors.gcd;
new_offset = a->offset + gcd_factors.x * delta_on_gcd * a->grain_size;
new_grain_size = a->grain_size * b->grain_size / gcd_factors.gcd;
/* inconsistency => no solution */
if (new_offset
!= b->offset - gcd_factors.y * delta_on_gcd * b->grain_size)
return NULL;
return ped_alignment_new (new_offset, new_grain_size);
}
/* This function returns the sector closest to "sector" that lies inside
* geom and satisfies the alignment constraint.
*/
static PedSector
_closest_inside_geometry (const PedAlignment* align, const PedGeometry* geom,
PedSector sector)
{
PED_ASSERT (align != NULL, return -1);
if (!align->grain_size) {
if (ped_alignment_is_aligned (align, geom, sector)
&& (!geom || ped_geometry_test_sector_inside (geom,
sector)))
return sector;
else
return -1;
}
if (sector < geom->start)
sector += ped_round_up_to (geom->start - sector,
align->grain_size);
if (sector > geom->end)
sector -= ped_round_up_to (sector - geom->end,
align->grain_size);
if (!ped_geometry_test_sector_inside (geom, sector))
return -1;
return sector;
}
/**
* This function returns the closest sector to \p sector that lies inside
* \p geom that satisfies the given alignment constraint \p align. It prefers
* sectors that are beyond \p sector (are not smaller than \p sector),
* but does not guarantee that this.
*
* \return a PedSector on success, \c -1 on failure
*/
PedSector
ped_alignment_align_up (const PedAlignment* align, const PedGeometry* geom,
PedSector sector)
{
PedSector result;
PED_ASSERT (align != NULL, return -1);
if (!align->grain_size)
result = align->offset;
else
result = ped_round_up_to (sector - align->offset,
align->grain_size)
+ align->offset;
if (geom)
result = _closest_inside_geometry (align, geom, result);
return result;
}
/**
* This function returns the closest sector to \p sector that lies inside
* \p geom that satisfies the given alignment constraint \p align. It prefers
* sectors that are before \p sector (are not larger than \p sector),
* but does not guarantee that this.
*
* \return a PedSector on success, \c -1 on failure
*/
PedSector
ped_alignment_align_down (const PedAlignment* align, const PedGeometry* geom,
PedSector sector)
{
PedSector result;
PED_ASSERT (align != NULL, return -1);
if (!align->grain_size)
result = align->offset;
else
result = ped_round_down_to (sector - align->offset,
align->grain_size)
+ align->offset;
if (geom)
result = _closest_inside_geometry (align, geom, result);
return result;
}
/* Returns either a or b, depending on which is closest to "sector". */
static PedSector
closest (PedSector sector, PedSector a, PedSector b)
{
if (a == -1)
return b;
if (b == -1)
return a;
if (abs (sector - a) < abs (sector - b))
return a;
else
return b;
}
/**
* This function returns the sector that is closest to \p sector,
* satisfies the \p align constraint and lies inside \p geom.
*
* \return a PedSector on success, \c -1 on failure
*/
PedSector
ped_alignment_align_nearest (const PedAlignment* align, const PedGeometry* geom,
PedSector sector)
{
PED_ASSERT (align != NULL, return -1);
return closest (sector, ped_alignment_align_up (align, geom, sector),
ped_alignment_align_down (align, geom, sector));
}
/**
* This function returns 1 if \p sector satisfies the alignment
* constraint \p align and lies inside \p geom.
*
* \return \c 1 on success, \c 0 on failure
*/
int
ped_alignment_is_aligned (const PedAlignment* align, const PedGeometry* geom,
PedSector sector)
{
if (!align)
return 0;
if (geom && !ped_geometry_test_sector_inside (geom, sector))
return 0;
if (align->grain_size)
return (sector - align->offset) % align->grain_size == 0;
else
return sector == align->offset;
}
/**
* @}
*/