#![allow(unsafe_code)]
pub use super::super::gmp::mpz::Mpz;
use super::super::gmp::mpz::{mp_bitcnt_t, mp_limb_t};
use libc::{c_int, c_long, c_ulong, c_void, size_t};
use std::{mem, usize};
#[link(name = "gmp")]
extern "C" {
fn __gmpz_gcdext(gcd: *mut Mpz, s: *mut Mpz, t: *mut Mpz, a: *const Mpz, b: *const Mpz);
fn __gmpz_gcd(rop: *mut Mpz, op1: *const Mpz, op2: *const Mpz);
fn __gmpz_fdiv_qr(q: *mut Mpz, r: *mut Mpz, b: *const Mpz, g: *const Mpz);
fn __gmpz_fdiv_q(q: *mut Mpz, n: *const Mpz, d: *const Mpz);
fn __gmpz_divexact(q: *mut Mpz, n: *const Mpz, d: *const Mpz);
fn __gmpz_tdiv_q(q: *mut Mpz, n: *const Mpz, d: *const Mpz);
fn __gmpz_mul(p: *mut Mpz, a: *const Mpz, b: *const Mpz);
fn __gmpz_mul_2exp(rop: *mut Mpz, op1: *const Mpz, op2: mp_bitcnt_t);
fn __gmpz_sub(rop: *mut Mpz, op1: *const Mpz, op2: *const Mpz);
fn __gmpz_import(
rop: *mut Mpz,
count: size_t,
order: c_int,
size: size_t,
endian: c_int,
nails: size_t,
op: *const c_void,
);
fn __gmpz_tdiv_r(r: *mut Mpz, n: *const Mpz, d: *const Mpz);
fn __gmpz_sizeinbase(op: &Mpz, base: c_int) -> size_t;
fn __gmpz_fdiv_q_ui(rop: *mut Mpz, op1: *const Mpz, op2: c_ulong) -> c_ulong;
fn __gmpz_add(rop: *mut Mpz, op1: *const Mpz, op2: *const Mpz);
fn __gmpz_add_ui(rop: *mut Mpz, op1: *const Mpz, op2: c_ulong);
fn __gmpz_set_ui(rop: &mut Mpz, op: c_ulong);
fn __gmpz_set_si(rop: &mut Mpz, op: c_long);
fn __gmpz_cdiv_ui(n: &Mpz, d: c_ulong) -> c_ulong;
fn __gmpz_fdiv_ui(n: &Mpz, d: c_ulong) -> c_ulong;
fn __gmpz_tdiv_ui(n: &Mpz, d: c_ulong) -> c_ulong;
fn __gmpz_export(
rop: *mut c_void,
countp: *mut size_t,
order: c_int,
size: size_t,
endian: c_int,
nails: size_t,
op: &Mpz,
) -> *mut c_void;
fn __gmpz_powm(rop: *mut Mpz, base: *const Mpz, exp: *const Mpz, modulus: *const Mpz);
}
#[repr(C)]
struct MpzStruct {
mp_alloc: c_int,
mp_size: c_int,
mp_d: *mut mp_limb_t,
}
macro_rules! impl_div_ui {
($t:ident, $i:ident, $f:expr) => {
pub fn $i(n: &Mpz, d: $t) -> $t {
use std::$t;
let res = unsafe { $f(n, c_ulong::from(d)) };
assert!(res <= $t::MAX.into());
res as $t
}
};
}
impl_div_ui!(u16, mpz_crem_u16, __gmpz_cdiv_ui);
impl_div_ui!(u32, mpz_frem_u32, __gmpz_fdiv_ui);
#[inline]
pub fn mpz_is_negative(z: &Mpz) -> bool {
unsafe { (*(z as *const _ as *const MpzStruct)).mp_size < 0 }
}
#[inline]
pub fn mpz_powm(rop: &mut Mpz, base: &Mpz, exponent: &Mpz, modulus: &Mpz) {
unsafe { __gmpz_powm(rop, base, exponent, modulus) }
}
#[inline]
pub fn mpz_tdiv_r(r: &mut Mpz, n: &Mpz, d: &Mpz) {
unsafe { __gmpz_tdiv_r(r, n, d) }
}
#[inline]
pub fn mpz_gcdext(gcd: &mut Mpz, s: &mut Mpz, t: &mut Mpz, a: &Mpz, b: &Mpz) {
unsafe { __gmpz_gcdext(gcd, s, t, a, b) }
}
#[inline]
pub fn mpz_double(rop: &mut Mpz) {
if true {
unsafe { __gmpz_mul_2exp(rop, rop, 1) }
} else {
unsafe { __gmpz_add(rop, rop, rop) }
}
}
#[inline]
pub fn mpz_fdiv_qr(q: &mut Mpz, r: &mut Mpz, b: &Mpz, g: &Mpz) {
unsafe { __gmpz_fdiv_qr(q, r, b, g) }
}
#[inline]
pub fn mpz_fdiv_q_ui_self(rop: &mut Mpz, op: c_ulong) -> c_ulong {
unsafe { __gmpz_fdiv_q_ui(rop, rop, op) }
}
pub fn import_obj(buf: &[u8]) -> Mpz {
fn raw_import(buf: &[u8]) -> Mpz {
let mut obj = Mpz::new();
unsafe { __gmpz_import(&mut obj, buf.len(), 1, 1, 1, 0, buf.as_ptr() as *const _) }
obj
}
let is_negative = match buf.first() {
None => return Mpz::zero(),
Some(x) => x & 0x80 != 0,
};
if !is_negative {
raw_import(buf)
} else {
let mut new_buf: Vec<_> = buf.iter().cloned().skip_while(|&x| x == 0xFF).collect();
if new_buf.is_empty() {
(-1).into()
} else {
for i in &mut new_buf {
*i ^= 0xFF
}
!raw_import(&new_buf)
}
}
}
pub fn three_gcd(rop: &mut Mpz, a: &Mpz, b: &Mpz, c: &Mpz) {
unsafe {
__gmpz_gcd(rop, a, b);
__gmpz_gcd(rop, rop, c)
}
}
#[inline]
pub fn size_in_bits(obj: &Mpz) -> usize {
unsafe { __gmpz_sizeinbase(obj, 2) }
}
#[inline]
pub fn mpz_add(rop: &mut Mpz, op1: &Mpz, op2: &Mpz) {
unsafe { __gmpz_add(rop, op1, op2) }
}
#[inline]
pub fn mpz_mul(rop: &mut Mpz, op1: &Mpz, op2: &Mpz) {
unsafe { __gmpz_mul(rop, op1, op2) }
}
#[inline]
pub fn mpz_divexact(q: &mut Mpz, n: &Mpz, d: &Mpz) {
unsafe { __gmpz_divexact(q, n, d) }
}
#[inline]
pub fn mpz_mul_2exp(rop: &mut Mpz, op1: &Mpz, op2: mp_bitcnt_t) {
unsafe { __gmpz_mul_2exp(rop as *mut _ as *mut Mpz, op1, op2) }
}
#[inline]
pub fn mpz_fdiv_q(q: &mut Mpz, n: &Mpz, d: &Mpz) {
if mpz_is_negative(n) == mpz_is_negative(d) {
unsafe { __gmpz_tdiv_q(q, n, d) }
} else {
unsafe { __gmpz_fdiv_q(q, n, d) }
}
}
#[inline]
#[cfg(none)]
pub fn mpz_neg(rop: &mut Mpz) {
assert!(mem::size_of::<Mpz>() == mem::size_of::<MpzStruct>());
unsafe {
let ptr = rop as *mut _ as *mut MpzStruct;
let v = (*ptr).mp_size;
(*ptr).mp_size = -v;
}
}
#[inline]
pub fn mpz_sub(rop: &mut Mpz, op1: &Mpz, op2: &Mpz) {
unsafe { __gmpz_sub(rop as *mut _ as *mut Mpz, op1, op2) }
}
pub fn export_obj(obj: &Mpz, v: &mut [u8]) -> Result<(), usize> {
unsafe fn raw_export(v: &mut [u8], offset: usize, obj: &Mpz) -> usize {
let ptr = v.as_mut_ptr().add(offset) as *mut c_void;
*(ptr as *mut u8) = 0;
let mut s: usize = mem::uninitialized();
let ptr2 = __gmpz_export(ptr, &mut s, 1, 1, 1, 0, obj);
assert_eq!(ptr, ptr2);
if 0 == s {
1
} else {
s
}
}
let size = size_in_bits(obj);
assert!(size > 0);
if size > usize::MAX - 8 || v.len() > usize::MAX >> 3 {
return Err(usize::MAX);
}
let byte_len_needed = (size + 8) >> 3;
if v.len() < byte_len_needed {
return if v.is_empty() && obj.is_zero() {
Ok(())
} else {
Err(byte_len_needed)
};
}
let is_negative = mpz_is_negative(obj);
if is_negative {
let obj = !obj;
debug_assert!(
!mpz_is_negative(&obj),
"bitwise negation of a negative number produced a negative number"
);
let new_byte_size = (size_in_bits(&obj) + 7) >> 3;
let offset = v.len() - new_byte_size;
for i in &mut v[..offset] {
*i = 0xFF
}
unsafe {
assert_eq!(raw_export(v, offset, &obj), new_byte_size);
}
for i in &mut v[offset..] {
*i ^= 0xFF
}
} else {
let byte_len = (size + 7) >> 3;
assert!(byte_len > 0);
let offset = v.len() - byte_len;
for i in &mut v[..offset] {
*i = 0
}
unsafe {
assert_eq!(raw_export(v, offset, &obj), byte_len);
}
}
Ok(())
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn check_expected_bit_width() {
let mut s: Mpz = (-2).into();
assert_eq!(size_in_bits(&s), 2);
s = !s;
assert_eq!(s, 1.into());
s.setbit(2);
assert_eq!(s, 5.into());
}
#[test]
fn check_export() {
let mut s: Mpz = 0x100.into();
s = !s;
let mut buf = [0, 0, 0];
export_obj(&s, &mut buf).expect("buffer should be large enough");
assert_eq!(buf, [0xFF, 0xFE, 0xFF]);
export_obj(&Mpz::zero(), &mut []).unwrap();
}
#[test]
fn check_rem() {
assert_eq!(mpz_crem_u16(&(-100i64).into(), 3), 1);
assert_eq!(mpz_crem_u16(&(100i64).into(), 3), 2);
}
}