pmt: initial 3.0.2 update
This commit is contained in:
481
jni/parted/libparted/cs/natmath.c
Executable file
481
jni/parted/libparted/cs/natmath.c
Executable file
@@ -0,0 +1,481 @@
|
||||
/*
|
||||
libparted - a library for manipulating disk partitions
|
||||
Copyright (C) 2000, 2007-2014, 2019-2023 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \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.
|
||||
*/
|
||||
static 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);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
PED_ASSERT (b >= 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);
|
||||
|
||||
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:
|
||||
free (align);
|
||||
error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free up memory associated with \p align.
|
||||
*/
|
||||
void
|
||||
ped_alignment_destroy (PedAlignment* align)
|
||||
{
|
||||
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
|
||||
*/
|
||||
static EuclidTriple _GL_ATTRIBUTE_PURE
|
||||
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 _GL_ATTRIBUTE_PURE
|
||||
_closest_inside_geometry (const PedAlignment* align, const PedGeometry* geom,
|
||||
PedSector sector)
|
||||
{
|
||||
PED_ASSERT (align != NULL);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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 (llabs (sector - a) < llabs (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 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
Reference in New Issue
Block a user