Skip to content

Bignum support #39

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 41 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
0a812be
bigint and bigrat term getters and unifiers
Oct 31, 2022
cfa63ec
put bignum support behind rug feature flag
Oct 31, 2022
04afe70
Merge branch 'master' into bignum
Nov 8, 2022
9f2d1ab
use thread state to do faster atom deserialization from swipl
Nov 8, 2022
39a0240
ci: install specific gnu target for Windows
rrooij Nov 9, 2022
11bbee5
Fix typo
rrooij Nov 9, 2022
87e1304
Explicity target windows-gnu
rrooij Nov 9, 2022
0d6fafc
use default gnu toolchain
rrooij Nov 9, 2022
70deb1e
Try to set path in windows CI
rrooij Nov 9, 2022
c176eac
Add gmp and m4
rrooij Nov 9, 2022
4e404ef
use msys2 shell properly
rrooij Nov 9, 2022
c0aaff5
Add cargo to path
rrooij Nov 9, 2022
90e2d59
Add missing slash
rrooij Nov 9, 2022
df50c71
Install exact packages as specified on gmp-mpfr-sys docs
rrooij Nov 9, 2022
016cd7c
gmp also needed?
rrooij Nov 9, 2022
c87f2b0
Use mingw gmp
rrooij Nov 9, 2022
4353db0
update: true
rrooij Nov 9, 2022
502f636
usr include
rrooij Nov 9, 2022
bf38a0a
Run msys2 by default on Windows
rrooij Nov 9, 2022
6e464f1
Close expression
rrooij Nov 9, 2022
ef91fe1
dmg setting
rrooij Nov 9, 2022
1b071a7
Try without prefix
rrooij Nov 9, 2022
d861b84
Set up specific packages
rrooij Nov 9, 2022
9443dea
Use cargo swipl for print_swipl_info
rrooij Nov 9, 2022
22f3b61
Specify swipl using ENV variable
rrooij Nov 9, 2022
5842396
ci: use windows paths
rrooij Nov 9, 2022
c274160
Directly use swipl
rrooij Nov 10, 2022
a225b57
Use different path
rrooij Nov 10, 2022
ca84099
Use swipl.exe
rrooij Nov 10, 2022
c08800a
Try again with different path
rrooij Nov 10, 2022
81a64e7
Merge branch 'master' into bignum
Nov 11, 2022
bbfb873
ensure gmp is built against the system library
Nov 11, 2022
820fb5c
disable optional features for the gmp-mpfr-sys library
Nov 11, 2022
1ee879f
try install mingw version for gmp for windows
Nov 11, 2022
81e2052
install pkg-config
Nov 11, 2022
055ca66
Merge branch 'master' into atom_serialization_shortcut
Nov 11, 2022
087bb0a
serialize atoms using their atom_t only in special cases
Nov 11, 2022
f698cae
change error back from debug version to normal
Nov 11, 2022
932c81e
Merge branch 'atom_serialization_shortcut' into bignum
Nov 15, 2022
21c31a6
Merge branch 'master' into bignum
Dec 8, 2022
b45f4c9
work
Jan 17, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 45 additions & 13 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,51 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
dmg: [no_dmg]
include:
- os: ubuntu-latest
shell: bash
dmg: no_dmg
- os: macos-latest
shell: bash
dmg: no_dmg
- os: windows-latest
shell: msys2
dmg: no_dmg

defaults:
run:
shell: ${{ matrix.shell}} {0}
#include:
# - os: macos-latest
# dmg: dmg
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
# This is needed because the GMP crate does not support MSVC
- name: Install SWI Prolog on Linux
if: ${{ matrix.os == 'ubuntu-latest' }}
run: |
sudo apt-add-repository ppa:swi-prolog/stable
sudo apt-get update
sudo apt install swi-prolog-nox
- name: Install LLVM and SWI Prolog on Windows

- name: Install msys2 for Windows
if: ${{ matrix.os == 'windows-latest' }}
run: choco install llvm swi-prolog
uses: msys2/setup-msys2@v2
with:
install: >-
diffutils
m4
make
gmp
gmp-devel
mingw-w64-x86_64-gcc
mingw-w64-x86_64-rust
mingw-w64-x86_64-swi-prolog
mingw-w64-clang-x86_64-toolchain
mingw-w64-x86_64-gmp
mingw-w64-x86_64-pkg-config

- name: Install SWI Prolog on MacOS from brew
if: ${{ matrix.os == 'macos-latest' && matrix.dmg == 'no_dmg' }}
run: brew install swi-prolog
Expand All @@ -44,27 +72,31 @@ jobs:
- name: Print information with swipl-info on posix
if: ${{ matrix.os != 'windows-latest' && matrix.dmg == 'no_dmg' }}
run: cargo run --bin print_swipl_info
- name: Install cargo-swipl
run: cargo install --path ./cargo-swipl
- name: Print information with swipl-info on windows
if: ${{ matrix.os == 'windows-latest' }}
env:
SWIPL: C:\Program Files\swipl\bin\swipl.exe
run: cargo run --bin print_swipl_info
env:
SWIPL: /mingw64/lib/swipl/bin/amd64-windows/swipl.exe
run: cargo swipl run --bin print_swipl_info
- name: Print information with swipl-info on osx with dmg
if: ${{ matrix.dmg == 'dmg' }}
env:
SWIPL: /Applications/SWI-Prolog.app/Contents/MacOS/swipl
run: cargo run --bin print_swipl_info
- name: Install cargo-swipl
run: cargo install --path ./cargo-swipl
- name: Run tests on posix
if: ${{ matrix.os != 'windows-latest' && matrix.dmg == 'no_dmg' }}
run: cargo swipl test --all-features

- name: Run tests on windows
if: ${{ matrix.os == 'windows-latest' }}
env:
SWIPL: C:\Program Files\swipl\bin\swipl.exe
LIBCLANG_PATH: C:\Program Files\LLVM\bin\
run: cargo swipl test --all-features
env:
C_INCLUDE_PATH: /usr/include
SWIPL: /mingw64/lib/swipl/bin/amd64-windows/swipl.exe
run: |
export C_INCLUDE_PATH=/usr/include/
cargo swipl test --all-features --target=x86_64-pc-windows-gnu

- name: Run tests on osx with dmg
if: ${{ matrix.dmg == 'dmg' }}
env:
Expand Down
3 changes: 3 additions & 0 deletions swipl-fli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@ documentation = "https://terminusdb-labs.github.io/swipl-rs/swipl_fli/"
[build-dependencies]
bindgen = "0.59.0"
swipl-info = {path = "../swipl-info", version = "0.3.2"}

[features]
gmp = []
7 changes: 6 additions & 1 deletion swipl-fli/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ fn main() {
println!("cargo:rerun-if-changed=c/wrapper.h");
println!("cargo:rerun-if-env-changed=SWIPL");

let bindings = bindgen::Builder::default()
let mut bindings = bindgen::Builder::default();
if cfg!(feature = "gmp") {
bindings = bindings.header_contents("include_gmp", "#include <gmp.h>\n");
}

let bindings = bindings
.header("c/wrapper.h")
.clang_arg(format!("-I{}", info.header_dir))
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
Expand Down
7 changes: 6 additions & 1 deletion swipl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ lazy_static = "1.4.0"
thiserror = "1.0"
serde = {version="1.0", optional=true}
convert_case = "0.6"
rug = {version = "1.17", default-features = false, optional=true, features=["integer","rational"]}
gmp-mpfr-sys = {version = "1.4", default-features = false, optional=true, features=["use-system-libs"]}

[dev-dependencies]
serde = {version="1.0", features=["derive"]}
serde = {version="1.0", features=["derive"]}

[features]
rug = ["dep:rug", "dep:gmp-mpfr-sys", "swipl-fli/gmp"]
14 changes: 7 additions & 7 deletions swipl/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ enum FrameState {
/// let term2 = frame.new_term_ref();
///
/// frame.close();
/// assert_eq!(42_u64, term.get()?);
/// assert_eq!(42_u64, term.get::<u64>()?);
/// // the following would result in a compile error:
/// // term2.unify(42_u64)?;
///
Expand All @@ -482,7 +482,7 @@ enum FrameState {
/// term.unify(43_u64)?;
///
/// frame.close();
/// assert_eq!(43_u64, term.get()?);
/// assert_eq!(43_u64, term.get::<u64>()?);
///
/// Ok(())
/// }
Expand Down Expand Up @@ -1207,7 +1207,7 @@ mod tests {
let next = query.next_solution()?;

assert!(!next);
assert_eq!(42_u64, term1.get()?);
assert_eq!(42_u64, term1.get::<u64>()?);

let next = query.next_solution();
assert!(next.is_err());
Expand Down Expand Up @@ -1239,7 +1239,7 @@ mod tests {
let next = query.next_solution()?;

assert!(!next);
assert_eq!(42_u64, term1.get().unwrap());
assert_eq!(42_u64, term1.get::<u64>().unwrap());
}

// after leaving the block, we have discarded
Expand Down Expand Up @@ -1272,7 +1272,7 @@ mod tests {
let next = query.next_solution()?;

assert!(!next);
assert_eq!(42_u64, term1.get()?);
assert_eq!(42_u64, term1.get::<u64>()?);
query.discard();
}

Expand Down Expand Up @@ -1306,12 +1306,12 @@ mod tests {
let next = query.next_solution()?;

assert!(!next);
assert_eq!(42_u64, term1.get()?);
assert_eq!(42_u64, term1.get::<u64>()?);
query.cut();
}

// a cut query leaves data intact
assert_eq!(42_u64, term1.get()?);
assert_eq!(42_u64, term1.get::<u64>()?);

Ok(())
}
Expand Down
4 changes: 2 additions & 2 deletions swipl/src/dict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -656,7 +656,7 @@ mod tests {
dict.unify(&builder).unwrap();

dict.get_dict_tag_term(&tag2).unwrap();
assert_eq!(42_u64, tag2.get().unwrap());
assert_eq!(42_u64, tag2.get::<u64>().unwrap());
tag.unify(&tag2).unwrap();
}

Expand All @@ -673,7 +673,7 @@ mod tests {

dict.unify(&builder).unwrap();

assert_eq!(42_u64, dict.get_dict_key("foo").unwrap());
assert_eq!(42_u64, dict.get_dict_key::<_, u64>("foo").unwrap());
let hello_str: String = dict.get_dict_key("bar").unwrap();
assert_eq!("hello", hello_str);

Expand Down
4 changes: 2 additions & 2 deletions swipl/src/functor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,12 +272,12 @@ mod tests {
assert!(term.unify(f).is_ok());
assert!(term.get_arg::<u64>(1).unwrap_err().is_failure());
assert!(term.unify_arg(1, 42_u64).is_ok());
assert_eq!(42_u64, term.get_arg(1).unwrap());
assert_eq!(42_u64, term.get_arg::<u64>(1).unwrap());
assert!(term.unify_arg(1, 42_u64).is_ok());
assert!(!term.unify_arg(1, 43_u64).is_ok());

assert!(term.unify_arg(2, 24_u64).is_ok());
assert_eq!(24_u64, term.get_arg(2).unwrap());
assert_eq!(24_u64, term.get_arg::<u64>(2).unwrap());

assert!(!term.unify_arg(3, 24_u64).is_ok());
assert!(term.get_arg::<u64>(3).unwrap_err().is_failure());
Expand Down
2 changes: 1 addition & 1 deletion swipl/src/result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::context::{Context, QueryableContextType};
/// This is either a failure or an exception. In case of an exception,
/// whowever returned the exception was also supposed to raise an
/// exception on the context.
#[derive(Error, Debug)]
#[derive(Error, Debug, PartialEq, Eq)]
pub enum PrologError {
#[error("prolog function failed")]
Failure,
Expand Down
163 changes: 163 additions & 0 deletions swipl/src/term/bignum.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
//! Support for rug Integer and Rational using SWI-Prolog's GMP support.

use crate::fli;
use crate::term::*;
use crate::{term_getable, unifiable};
use rug::{Integer, Rational};

term_getable! {
(Integer, "bigint", term) => {
let mut i = Integer::new();
let ptr = i.as_raw_mut();

let result = unsafe {
fli::PL_get_mpz(term.term_ptr(), std::mem::transmute(ptr))
};
if result != 0 {
Some(i)
}
else {
None
}
}
}

unifiable! {
(self:&Integer, term) => {
let ptr = self.as_raw();

let result = unsafe {
fli::PL_unify_mpz(term.term_ptr(), std::mem::transmute(ptr))
};

result != 0
}
}

term_getable! {
(Rational, "bigrat", term) => {
eprintln!("hi");
let mut r = Rational::new();
let ptr = r.as_raw_mut();

let result = unsafe {
fli::PL_get_mpq(term.term_ptr(), std::mem::transmute(ptr))
};
if result != 0 {
Some(r)
}
else {
None
}
}
}

unifiable! {
(self:&Rational, term) => {
let ptr = self.as_raw();

let result = unsafe {
fli::PL_unify_mpq(term.term_ptr(), std::mem::transmute(ptr))
};

result != 0
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::context::*;
use std::str::FromStr;
#[test]
fn get_bigint() {
let engine = Engine::new();
let activation = engine.activate();
let context: Context<_> = activation.into();

let big_int_str = "1234123412341234123412341234123412341234";
let term = context.term_from_string(big_int_str).unwrap();

let expected = Integer::from_str(big_int_str).unwrap();
let i: Integer = term.get().unwrap();
assert_eq!(expected, i);
}

#[test]
fn unify_bigint() {
let engine = Engine::new();
let activation = engine.activate();
let context: Context<_> = activation.into();

let big_int_str = "1234123412341234123412341234123412341234";
let i = Integer::from_str(big_int_str).unwrap();

let term = context.new_term_ref();
term.unify(&i).unwrap();

let s = context.string_from_term(&term).unwrap();

assert_eq!(big_int_str, s);
}

#[test]
fn get_bigrat() {
let engine = Engine::new();
let activation = engine.activate();
let context: Context<_> = activation.into();

// we construct a big rational in swipl using the 'r' notation.
// We use a prime denominator to ensure no unexpected normalizing happens.
let big_numerator = "1234123412341234123412341234123412341234";
let big_prime_str = "19131612631094571991039";
let big_rat_prolog_string = format!("{big_numerator}r{big_prime_str}");
let big_rat_div_string = format!("{big_numerator}/{big_prime_str}");
let term = context.term_from_string(&big_rat_prolog_string).unwrap();

let expected = Rational::from_str(&big_rat_div_string).unwrap();
let i: Rational = term.get().unwrap();
assert_eq!(expected, i);
}

#[test]
fn unify_bigrat() {
let engine = Engine::new();
let activation = engine.activate();
let context: Context<_> = activation.into();

let big_numerator = "1234123412341234123412341234123412341234";
let big_prime_str = "19131612631094571991039";
let big_rat_prolog_string = format!("{big_numerator}r{big_prime_str}");
let big_rat_div_string = format!("{big_numerator}/{big_prime_str}");
let i = Rational::from_str(&big_rat_div_string).unwrap();

let term = context.new_term_ref();
term.unify(&i).unwrap();

let s = context.string_from_term(&term).unwrap();

assert_eq!(big_rat_prolog_string, s);
}

#[test]
fn unify_unequal_bigrat_fails() {
let engine = Engine::new();
let activation = engine.activate();
let context: Context<_> = activation.into();

let big_numerator = "1234123412341234123412341234123412341234";
let big_prime_str = "19131612631094571991039";
let big_rat_prolog_string = format!("{big_numerator}r{big_prime_str}");
let big_rat_div_string = format!("{big_numerator}/{big_prime_str}");
let i = Rational::from_str(&big_rat_div_string).unwrap();
let i2: Rational = i.clone() + 1;

let term = context.new_term_ref();
term.unify(&i).unwrap();
assert!(term.unify(&i2).unwrap_err().is_failure());

let s = context.string_from_term(&term).unwrap();

assert_eq!(big_rat_prolog_string, s);
}
}
Loading