Skip to content
31 changes: 31 additions & 0 deletions src/ffi.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,38 @@
use mpz::*;
use std::{mem, slice, str};
use libc::{c_char, strlen};

type AllocFunc = extern "C" fn(usize) -> *mut c_char;
type ReallocFunc = extern "C" fn(*mut c_char, usize, usize) -> *mut c_char;
type FreeFunc = extern "C" fn(*mut c_char, usize);

#[link(name = "gmp")]
extern "C" {
pub fn __gmpz_fdiv_q(q: mpz_ptr, n: mpz_srcptr, d: mpz_srcptr);
pub fn __gmpz_cdiv_q(q: mpz_ptr, n: mpz_srcptr, d: mpz_srcptr);
pub fn __gmp_get_memory_functions(alloc: *mut AllocFunc, relloc: *mut ReallocFunc, free: *mut FreeFunc);
}

pub struct GString(*mut u8, usize);

impl GString {
pub unsafe fn from_raw(raw: *mut c_char) -> GString {
GString(raw as *mut u8, strlen(raw) as usize + 1)
}

pub fn to_str(&self) -> Result<&str, str::Utf8Error> {
let bytes: &[u8] = unsafe { slice::from_raw_parts(self.0, self.1) };
str::from_utf8(&bytes[..bytes.len() - 1])
}
}

impl Drop for GString {
fn drop(&mut self) {
use std::ptr::null_mut;
unsafe {
let mut free_func: FreeFunc = mem::uninitialized();
__gmp_get_memory_functions(null_mut(), null_mut(), &mut free_func);
free_func(self.0 as *mut c_char, self.1);
}
}
}
60 changes: 51 additions & 9 deletions src/mpf.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use libc::{c_double, c_int, c_long, c_ulong, c_void,c_char};
use libc::{c_double, c_int, c_long, c_ulong, c_void, c_char, strlen};
use std::mem::uninitialized;
use std::cmp;
use std::{cmp, fmt, str};
use std::cmp::Ordering::{self, Greater, Less, Equal};
use std::ops::{Div, DivAssign, Mul, MulAssign, Add, AddAssign, Sub, SubAssign, Neg};
use std::ffi::CString;
Expand All @@ -10,6 +10,7 @@ use super::mpz::{Mpz, mpz_srcptr};
use super::mpq::{Mpq, mpq_srcptr};
use super::sign::Sign;
use num_traits::{Zero, One};
use ffi::GString;

type mp_exp_t = c_long;

Expand Down Expand Up @@ -37,7 +38,7 @@ extern "C" {

fn __gmpf_set_str(rop: mpf_ptr, str: *const c_char, base: c_int);
fn __gmpf_set_si(rop: mpf_ptr, op: c_long);
fn __gmpf_get_str(str: *const c_char, expptr: *const mp_exp_t, base: i32, n_digits: i32, op: mpf_ptr) -> *mut c_char;
fn __gmpf_get_str(str: *mut c_char, expptr: *const mp_exp_t, base: i32, n_digits: i32, op: mpf_srcptr) -> *mut c_char;

fn __gmpf_cmp(op1: mpf_srcptr, op2: mpf_srcptr) -> c_int;
fn __gmpf_cmp_d(op1: mpf_srcptr, op2: c_double) -> c_int;
Expand Down Expand Up @@ -119,13 +120,27 @@ impl Mpf {
}
}

pub fn get_str(&mut self, n_digits: i32, base: i32, exp: &mut c_long) -> String{
let c_str = CString::new("").unwrap();
let out;
unsafe{
out = CString::from_raw(__gmpf_get_str(c_str.into_raw(), exp, base, n_digits, &mut self.mpf));
/// For values of zero, this will return an empty string.
pub fn get_str(&self, n_digits: i32, base: i32, exp: &mut c_long) -> String {
use std::ptr::null_mut;
if n_digits == 0 {
// Maximal significant digits requested. Let GMP compute the length
// and allocate the space required.
unsafe {
let p = __gmpf_get_str(null_mut(), exp, base, n_digits, &self.mpf);
// De-allocates the GMP string on drop.
GString::from_raw(p).to_str().unwrap().to_string()
}
} else {
unsafe {
// n_digits + 2 from mpf/get_str.c.
let mut bytes: Vec<u8> = vec![0; n_digits as usize + 2];
let c_str: *mut c_char = bytes.as_mut_ptr() as *mut c_char;
__gmpf_get_str(c_str, exp, base, n_digits, &self.mpf);
// Don't include null bytes.
String::from(str::from_utf8(&bytes[..strlen(c_str)]).unwrap())
}
}
out.to_str().unwrap().to_string()
}

pub fn abs(&self) -> Mpf {
Expand Down Expand Up @@ -204,6 +219,33 @@ impl Clone for Mpf {
}
}

impl fmt::Display for Mpf {
/// Confer the note for `fmt::Debug::fmt`.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
<Self as fmt::Debug>::fmt(self, f)
}
}

impl fmt::Debug for Mpf {
/// Due to the way `Mpf::get_str` works, the output contains an implicit
/// radix point to the left of the first digit. `3.14`, for instance, will
/// print as `0.314e1`.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut exp: c_long = 0;
match self.get_str(0, 10, &mut exp) {
ref n if n.len() == 0 => write!(f, "0"),
n => {
if self.sign() == Sign::Negative {
// Skip sign output
write!(f, "-0.{}e{}", &n[1..], exp)
} else {
write!(f, "0.{}e{}", n, exp)
}
}
}
}
}

impl Eq for Mpf { }
impl PartialEq for Mpf {
fn eq(&self, other: &Mpf) -> bool {
Expand Down
29 changes: 29 additions & 0 deletions src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -713,4 +713,33 @@ mod mpf {
assert_eq!(five.sign(), Sign::Positive);
assert_eq!(minus_five.sign(), Sign::Negative);
}

#[test]
fn test_get_str() {
use libc::c_long;
let mut tmp = 0 as c_long;
let mut pi: Mpf = Mpf::zero();
assert_eq!(pi.get_str(4, 10, &mut tmp), "");
pi.set_from_str("3.141592653589", 10);

assert_eq!(&pi.get_str(4, 10, &mut tmp), "3142");
assert_eq!((-&pi).get_str(4, 10, &mut tmp), "-3142");
}

#[test]
fn test_fmt() {
let mut pi: Mpf = Mpf::zero();
assert_eq!(format!("{:?}", pi), "0");
assert_eq!(format!("{:?}", -&pi), "0");

pi.set_from_str("3.141592653589", 10);

// From the GNU MP Section 7.4: "The generated string is a fraction,
// with an implicit radix point immediately to the left of the first
// digit. The applicable exponent is written through the expptr pointer.
// For example, the number 3.1416 would be returned as string "31416"
// and exponent 1."
assert_eq!(format!("{:?}", pi), "0.3141592653589e1");
assert_eq!(format!("{:?}", -&pi), "-0.3141592653589e1");
}
}