Refactor & Connect to mercator_db
* Refactor the code, Update to the Service API REST JSON objects on disk. * Connect to the DB data model and engine to execute queries. * Remove the syntactic sugar around implicit `inside` operation on shapes, as it introduces issues.
This commit is contained in:
@@ -1,57 +0,0 @@
|
||||
use crate::executors::ResultSet;
|
||||
use crate::symbols::*;
|
||||
|
||||
//use ironsea_index::*;
|
||||
|
||||
pub fn get_all(_space_id: &String) -> ResultSet {
|
||||
//space::get_all(space_id)
|
||||
Err("not yet implemented".to_string())
|
||||
}
|
||||
|
||||
pub fn get_by_bounding_box(_space_id: &String, _bounding_box: &Vec<LiteralPosition>) -> ResultSet {
|
||||
Err("not yet implemented".to_string())
|
||||
}
|
||||
|
||||
pub fn get_by_position(_space_id: &String, _position: &LiteralPosition) -> ResultSet {
|
||||
Err("not yet implemented".to_string())
|
||||
}
|
||||
|
||||
pub fn get(_point: &Object, _fields: &Vec<Field>) -> Result<LiteralPosition, String> {
|
||||
Err("not yet implemented".to_string())
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
|
||||
pub struct Object {
|
||||
id: u32,
|
||||
}
|
||||
|
||||
impl Object {
|
||||
pub fn length(&self) -> f64 {
|
||||
0.0
|
||||
}
|
||||
|
||||
pub fn eval(&self, _predicate: &Predicate) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl Position {
|
||||
pub fn eval(&self, point: &Object) -> LiteralPosition {
|
||||
match self {
|
||||
Position::StrCmpICase(_selector, _string) => LiteralPosition(vec![]), //TODO
|
||||
Position::StrCmp(_selector, _string) => LiteralPosition(vec![]), //TODO
|
||||
Position::Selector(selector) => selector.eval(point),
|
||||
Position::Literal(position) => position.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LiteralSelector {
|
||||
pub fn eval(&self, point: &Object) -> LiteralPosition {
|
||||
let LiteralSelector(fields) = self;
|
||||
match get(point, fields) {
|
||||
Err(_) => LiteralPosition(vec![]),
|
||||
Ok(p @ LiteralPosition(_)) => p,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::types::LiteralTypes;
|
||||
|
||||
//FIXME: Improve, as this should not be static, but coming from DB.
|
||||
lazy_static! {
|
||||
static ref UNIVERSE: String = "Universe".to_string();
|
||||
static ref SPACES: HashMap<&'static str, Space> = {
|
||||
let mut m = HashMap::new();
|
||||
m.insert(
|
||||
"Universe",
|
||||
Space {
|
||||
space_type: LiteralTypes::Vector(vec![
|
||||
LiteralTypes::Float,
|
||||
LiteralTypes::Float,
|
||||
LiteralTypes::Float,
|
||||
]),
|
||||
bounding_box: vec![
|
||||
vec![0, 0, 0],
|
||||
vec![
|
||||
std::u32::MAX as i64,
|
||||
std::u32::MAX as i64,
|
||||
std::u32::MAX as i64,
|
||||
],
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
m
|
||||
};
|
||||
}
|
||||
|
||||
struct Space {
|
||||
space_type: LiteralTypes,
|
||||
bounding_box: Vec<Vec<i64>>,
|
||||
}
|
||||
|
||||
impl Space {
|
||||
pub fn max_volume(&self) -> f64 {
|
||||
let mut volume = 1.0;
|
||||
for max in &self.bounding_box[1] {
|
||||
volume *= *max as f64;
|
||||
}
|
||||
|
||||
volume
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name() -> &'static String {
|
||||
lazy_static! {
|
||||
static ref UNIVERSE: String = "Universe".to_string();
|
||||
};
|
||||
|
||||
&UNIVERSE
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_type(space_id: &String) -> &LiteralTypes {
|
||||
lazy_static! {
|
||||
static ref EMPTY_TYPE: LiteralTypes = LiteralTypes::Vector(Vec::new());
|
||||
};
|
||||
|
||||
match SPACES.get(space_id.as_str()) {
|
||||
None => &EMPTY_TYPE,
|
||||
Some(space) => &space.space_type,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn bounding_box(space_id: &String) -> &Vec<Vec<i64>> {
|
||||
lazy_static! {
|
||||
static ref EMPTY_BOX: Vec<Vec<i64>> = Vec::new();
|
||||
};
|
||||
match SPACES.get(space_id.as_str()) {
|
||||
None => &EMPTY_BOX,
|
||||
Some(space) => &space.bounding_box,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn max_volume(space_id: &String) -> f64 {
|
||||
match SPACES.get(space_id.as_str()) {
|
||||
None => 0.0,
|
||||
Some(space) => space.max_volume(),
|
||||
}
|
||||
}
|
||||
17
src/evaluators.rs
Normal file
17
src/evaluators.rs
Normal file
@@ -0,0 +1,17 @@
|
||||
use mercator_db::SpaceObject;
|
||||
|
||||
use super::expressions::*;
|
||||
use super::symbols::*;
|
||||
|
||||
impl Evaluator for Predicate {
|
||||
fn eval(&self, object: &SpaceObject) -> bool {
|
||||
match self {
|
||||
Predicate::Not(predicate) => !predicate.eval(object),
|
||||
Predicate::And(lh, rh) => lh.eval(object) && rh.eval(object),
|
||||
Predicate::Or(lh, rh) => lh.eval(object) || rh.eval(object),
|
||||
Predicate::Less(selector, literal) => &selector.value(object) < literal,
|
||||
Predicate::Greater(selector, literal) => &selector.value(object) > literal,
|
||||
Predicate::Equal(selector, literal) => &selector.value(object) == literal,
|
||||
}
|
||||
}
|
||||
}
|
||||
650
src/executors.rs
650
src/executors.rs
@@ -1,314 +1,384 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use super::database;
|
||||
use super::expression::*;
|
||||
use mercator_db::space;
|
||||
use mercator_db::Core;
|
||||
use mercator_db::DataBase;
|
||||
use mercator_db::SpaceObject;
|
||||
|
||||
use super::expressions::*;
|
||||
use super::symbols::*;
|
||||
|
||||
pub type ResultSet = Result<Vec<database::Object>, String>;
|
||||
impl From<&LiteralPosition> for space::Position {
|
||||
fn from(literal: &LiteralPosition) -> Self {
|
||||
let v: Vec<f64> = literal.into();
|
||||
v.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&LiteralNumber> for space::Coordinate {
|
||||
fn from(literal: &LiteralNumber) -> Self {
|
||||
match literal {
|
||||
LiteralNumber::Float(f) => (*f).into(),
|
||||
LiteralNumber::Int(i) => (*i as u64).into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn complement_helper(
|
||||
db: &DataBase,
|
||||
core: &Core,
|
||||
space_id: &str,
|
||||
inside: Vec<SpaceObject>,
|
||||
output_space: Option<&str>,
|
||||
threshold: f64,
|
||||
) -> mercator_db::ResultSet {
|
||||
let (low, high) = db.space(space_id)?.bounding_box();
|
||||
match core.get_by_shape(
|
||||
db,
|
||||
&space::Shape::BoundingBox(low, high),
|
||||
space_id,
|
||||
output_space,
|
||||
threshold,
|
||||
) {
|
||||
e @ Err(_) => e,
|
||||
Ok(points) => Ok(points
|
||||
.into_iter()
|
||||
.filter(|o| !inside.contains(&o))
|
||||
.collect::<Vec<_>>()),
|
||||
}
|
||||
}
|
||||
|
||||
fn distinct(
|
||||
db: &DataBase,
|
||||
core_id: &str,
|
||||
bag: &Bag,
|
||||
output_space: Option<&str>,
|
||||
threshold_volume: Option<f64>,
|
||||
) -> mercator_db::ResultSet {
|
||||
match bag.execute(db, core_id, output_space, threshold_volume) {
|
||||
e @ Err(_) => e,
|
||||
Ok(mut v) => {
|
||||
let set: HashSet<_> = v.drain(..).collect(); // dedup
|
||||
v.extend(set.into_iter());
|
||||
|
||||
Ok(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn filter(
|
||||
db: &DataBase,
|
||||
core_id: &str,
|
||||
predicate: &Option<Predicate>,
|
||||
bag: &Bag,
|
||||
output_space: Option<&str>,
|
||||
threshold_volume: Option<f64>,
|
||||
) -> mercator_db::ResultSet {
|
||||
match predicate {
|
||||
None => bag.execute(db, core_id, output_space, threshold_volume),
|
||||
Some(predicate) => match bag.execute(db, core_id, output_space, threshold_volume) {
|
||||
e @ Err(_) => e,
|
||||
Ok(results) => Ok(results
|
||||
.into_iter()
|
||||
.filter(|o| predicate.eval(&o))
|
||||
.collect::<Vec<_>>()),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn complement(
|
||||
db: &DataBase,
|
||||
core_id: &str,
|
||||
core: &Core,
|
||||
bag: &Bag,
|
||||
output_space: Option<&str>,
|
||||
threshold: f64,
|
||||
threshold_volume: Option<f64>,
|
||||
) -> mercator_db::ResultSet {
|
||||
match bag.execute(db, core_id, output_space, threshold_volume) {
|
||||
// FIXME: The complement of a set is computed within its definition space.
|
||||
e @ Err(_) => e,
|
||||
Ok(inside) => complement_helper(
|
||||
db,
|
||||
core,
|
||||
mercator_db::space::Space::universe().name(),
|
||||
inside,
|
||||
output_space,
|
||||
threshold,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn intersection(
|
||||
db: &DataBase,
|
||||
core_id: &str,
|
||||
rh: &Bag,
|
||||
lh: &Bag,
|
||||
output_space: Option<&str>,
|
||||
threshold_volume: Option<f64>,
|
||||
) -> mercator_db::ResultSet {
|
||||
let l = lh.execute(db, core_id, output_space, threshold_volume);
|
||||
if let Ok(l) = l {
|
||||
let r = rh.execute(db, core_id, output_space, threshold_volume);
|
||||
if let Ok(r) = r {
|
||||
let mut v = vec![];
|
||||
|
||||
if rh.predict(db) < lh.predict(db) {
|
||||
for o in r {
|
||||
if l.contains(&o) {
|
||||
v.push(o);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for o in l {
|
||||
if r.contains(&o) {
|
||||
v.push(o);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(v)
|
||||
} else {
|
||||
r
|
||||
}
|
||||
} else {
|
||||
l
|
||||
}
|
||||
}
|
||||
|
||||
fn union(
|
||||
db: &DataBase,
|
||||
core_id: &str,
|
||||
rh: &Bag,
|
||||
lh: &Bag,
|
||||
output_space: Option<&str>,
|
||||
threshold_volume: Option<f64>,
|
||||
) -> mercator_db::ResultSet {
|
||||
let l = lh.execute(db, core_id, output_space, threshold_volume);
|
||||
if let Ok(mut l) = l {
|
||||
let r = rh.execute(db, core_id, output_space, threshold_volume);
|
||||
if let Ok(mut r) = r {
|
||||
if rh.predict(db) < lh.predict(db) {
|
||||
l.append(&mut r);
|
||||
Ok(l)
|
||||
} else {
|
||||
r.append(&mut l);
|
||||
Ok(r)
|
||||
}
|
||||
} else {
|
||||
r
|
||||
}
|
||||
} else {
|
||||
l
|
||||
}
|
||||
}
|
||||
|
||||
fn bag(
|
||||
db: &DataBase,
|
||||
core_id: &str,
|
||||
bags: &[Bag],
|
||||
output_space: Option<&str>,
|
||||
threshold_volume: Option<f64>,
|
||||
) -> mercator_db::ResultSet {
|
||||
let mut v = vec![];
|
||||
for bag in bags {
|
||||
let b = bag.execute(db, core_id, output_space, threshold_volume);
|
||||
match b {
|
||||
e @ Err(_) => {
|
||||
return e;
|
||||
}
|
||||
Ok(mut b) => {
|
||||
v.append(&mut b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
fn inside(
|
||||
db: &DataBase,
|
||||
core: &Core,
|
||||
shape: &Shape,
|
||||
output_space: Option<&str>,
|
||||
threshold: f64,
|
||||
) -> mercator_db::ResultSet {
|
||||
let parameters = match shape {
|
||||
Shape::Point(space_id, position) => {
|
||||
let space = db.space(space_id)?;
|
||||
let position: Vec<f64> = position.into();
|
||||
let position = space.encode(&position)?;
|
||||
Ok((space_id, space::Shape::Point(position)))
|
||||
}
|
||||
Shape::HyperRectangle(space_id, bounding_box) => {
|
||||
if bounding_box.len() != 2 {
|
||||
Err("The number of position is different from 2, which is unsupported.".to_string())
|
||||
} else {
|
||||
let space = db.space(space_id)?;
|
||||
let low: Vec<f64> = (&bounding_box[0]).into();
|
||||
let high: Vec<f64> = (&bounding_box[1]).into();
|
||||
let low = space.encode(&low)?;
|
||||
let high = space.encode(&high)?;
|
||||
|
||||
Ok((space_id, space::Shape::BoundingBox(low, high)))
|
||||
}
|
||||
}
|
||||
Shape::HyperSphere(space_id, position, radius) => {
|
||||
let space = db.space(space_id)?;
|
||||
let position: Vec<f64> = position.into();
|
||||
let position = space.encode(&position)?;
|
||||
let mut r = vec![];
|
||||
for _ in 0..position.dimensions() {
|
||||
r.push(radius.into());
|
||||
}
|
||||
let radius = space.encode(&r)?[0];
|
||||
|
||||
//FIXME: RADIUS IS A LENGTH, HOW TO ENCODE IT INTO THE SPACE?
|
||||
Ok((space_id, space::Shape::HyperSphere(position, radius)))
|
||||
}
|
||||
Shape::Nifti(_space_id) => Err("Inside-Nifti: not yet implemented".to_string()),
|
||||
};
|
||||
|
||||
match parameters {
|
||||
Ok((space_id, shape)) => core.get_by_shape(db, &shape, space_id, output_space, threshold),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
fn outside(
|
||||
db: &DataBase,
|
||||
core: &Core,
|
||||
shape: &Shape,
|
||||
output_space: Option<&str>,
|
||||
threshold: f64,
|
||||
) -> mercator_db::ResultSet {
|
||||
match shape {
|
||||
Shape::Point(space_id, position) => {
|
||||
let position: Vec<f64> = position.into();
|
||||
match core.get_by_positions(db, &[position.into()], space_id, output_space, threshold) {
|
||||
e @ Err(_) => e,
|
||||
Ok(inside) => {
|
||||
complement_helper(db, core, space_id, inside, output_space, threshold)
|
||||
}
|
||||
}
|
||||
}
|
||||
Shape::HyperRectangle(space_id, bounding_box) => {
|
||||
// We need to adapt the bounding_box to ensure the
|
||||
// surface will not hit as part of the inside set, so we
|
||||
// compute the biggest bounding box contained within the
|
||||
// given box.
|
||||
|
||||
// Smallest increment possible
|
||||
let mut increment = Vec::with_capacity(bounding_box[0].dimensions());
|
||||
for _ in 0..bounding_box[0].dimensions() {
|
||||
increment.push(std::f64::EPSILON);
|
||||
}
|
||||
|
||||
// Add it to the lower bound
|
||||
let mut low: space::Position = (&bounding_box[0]).into();
|
||||
low += increment.clone().into();
|
||||
|
||||
// Substract it from the upper bound
|
||||
let mut high: space::Position = (&bounding_box[1]).into();
|
||||
high -= increment.into();
|
||||
|
||||
match core.get_by_shape(
|
||||
db,
|
||||
&space::Shape::BoundingBox(low, high),
|
||||
space_id,
|
||||
output_space,
|
||||
threshold,
|
||||
) {
|
||||
e @ Err(_) => e,
|
||||
Ok(inside) => {
|
||||
complement_helper(db, core, space_id, inside, output_space, threshold)
|
||||
}
|
||||
}
|
||||
}
|
||||
Shape::HyperSphere(space_id, center, radius) => {
|
||||
// Smallest decrement possible, to exclude the surface
|
||||
let mut radius: f64 = radius.into();
|
||||
radius -= std::f64::EPSILON;
|
||||
let center: space::Position = center.into();
|
||||
|
||||
match core.get_by_shape(
|
||||
db,
|
||||
&space::Shape::HyperSphere(center, radius.into()),
|
||||
space_id,
|
||||
output_space,
|
||||
threshold,
|
||||
) {
|
||||
e @ Err(_) => e,
|
||||
Ok(inside) => {
|
||||
complement_helper(db, core, space_id, inside, output_space, threshold)
|
||||
}
|
||||
}
|
||||
}
|
||||
Shape::Nifti(_space_id) => Err("Outside-nifti: not yet implemented".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
impl Executor for Projection {
|
||||
type ResultSet = self::ResultSet;
|
||||
type ResultSet = mercator_db::ResultSet;
|
||||
|
||||
fn execute(&self) -> ResultSet {
|
||||
fn execute(
|
||||
&self,
|
||||
db: &DataBase,
|
||||
core_id: &str,
|
||||
output_space: Option<&str>,
|
||||
threshold_volume: Option<f64>,
|
||||
) -> Self::ResultSet {
|
||||
match self {
|
||||
Projection::Nifti(_, _, _bag) => Err("not yet implemented".to_string()),
|
||||
Projection::JSON(_, _format, bag) => bag.execute(), // FIXME: Add projections here
|
||||
Projection::Nifti(_, _, _bag) => Err("Proj-Nifti: not yet implemented".to_string()),
|
||||
Projection::JSON(_, _format, bag) => {
|
||||
bag.execute(db, core_id, output_space, threshold_volume)
|
||||
// FIXME: Add projections here
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Executor for Bag {
|
||||
type ResultSet = self::ResultSet;
|
||||
type ResultSet = mercator_db::ResultSet;
|
||||
|
||||
fn execute(&self) -> ResultSet {
|
||||
fn get_bounding_box(
|
||||
position: &LiteralPosition,
|
||||
radius: &LiteralNumber,
|
||||
) -> Result<Vec<LiteralPosition>, String> {
|
||||
let LiteralPosition(position) = position;
|
||||
let mut low = vec![];
|
||||
let mut high = vec![];
|
||||
match radius {
|
||||
LiteralNumber::Int(r) => {
|
||||
for x in position {
|
||||
match x {
|
||||
LiteralNumber::Int(x) => {
|
||||
low.push(LiteralNumber::Int(x - r));
|
||||
high.push(LiteralNumber::Int(x + r));
|
||||
}
|
||||
LiteralNumber::Float(x) => {
|
||||
low.push(LiteralNumber::Float(x - (*r as f64)));
|
||||
high.push(LiteralNumber::Float(x + (*r as f64)));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
LiteralNumber::Float(r) => {
|
||||
for x in position {
|
||||
match x {
|
||||
LiteralNumber::Int(_) => {
|
||||
return Err(format!("The radius provided is a floating point value, which is incompatible with integer coordinates components: radius {:?}, coordinates {:?}", radius, position));
|
||||
}
|
||||
LiteralNumber::Float(x) => {
|
||||
low.push(LiteralNumber::Float(x - r));
|
||||
high.push(LiteralNumber::Float(x + r));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(vec![LiteralPosition(low), LiteralPosition(high)])
|
||||
fn execute(
|
||||
&self,
|
||||
db: &DataBase,
|
||||
core_id: &str,
|
||||
output_space: Option<&str>,
|
||||
threshold_volume: Option<f64>,
|
||||
) -> Self::ResultSet {
|
||||
let threshold = match threshold_volume {
|
||||
None => 0.0,
|
||||
Some(v) => v,
|
||||
};
|
||||
let core = db.core(core_id)?;
|
||||
|
||||
match self {
|
||||
Bag::Distinct(bag) => match bag.execute() {
|
||||
e @ Err(_) => e,
|
||||
Ok(mut v) => {
|
||||
let set: HashSet<_> = v.drain(..).collect(); // dedup
|
||||
v.extend(set.into_iter());
|
||||
|
||||
Ok(v)
|
||||
}
|
||||
},
|
||||
Bag::Filter(predicate, bag) => match predicate {
|
||||
None => bag.execute(),
|
||||
Some(predicate) => match bag.execute() {
|
||||
e @ Err(_) => e,
|
||||
Ok(source) => {
|
||||
let mut filtered = Vec::new();
|
||||
|
||||
for point in source {
|
||||
if point.eval(predicate) {
|
||||
filtered.push(point);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(filtered)
|
||||
}
|
||||
},
|
||||
},
|
||||
Bag::Complement(bag) => match bag.execute() {
|
||||
// The complement of a set is computed within its definition space.
|
||||
e @ Err(_) => e,
|
||||
Ok(inside) => {
|
||||
let mut outside = Vec::new();
|
||||
match database::get_all(bag.space()) {
|
||||
e @ Err(_) => e,
|
||||
Ok(points) => {
|
||||
for point in points {
|
||||
if !inside.contains(&point) {
|
||||
outside.push(point)
|
||||
}
|
||||
}
|
||||
|
||||
Ok(outside)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Bag::Distinct(bag) => distinct(db, core_id, bag, output_space, threshold_volume),
|
||||
Bag::Filter(predicate, bag) => {
|
||||
filter(db, core_id, predicate, bag, output_space, threshold_volume)
|
||||
}
|
||||
Bag::Complement(bag) => complement(
|
||||
db,
|
||||
core_id,
|
||||
core,
|
||||
bag,
|
||||
output_space,
|
||||
threshold,
|
||||
threshold_volume,
|
||||
),
|
||||
Bag::Intersection(lh, rh) => {
|
||||
let l = lh.execute();
|
||||
if let Ok(l) = l {
|
||||
let r = rh.execute();
|
||||
if let Ok(r) = r {
|
||||
let mut v = vec![];
|
||||
|
||||
if rh.predict() < lh.predict() {
|
||||
for o in r {
|
||||
if l.contains(&o) {
|
||||
v.push(o);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for o in l {
|
||||
if r.contains(&o) {
|
||||
v.push(o);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(v)
|
||||
} else {
|
||||
r
|
||||
}
|
||||
} else {
|
||||
l
|
||||
}
|
||||
intersection(db, core_id, rh, lh, output_space, threshold_volume)
|
||||
}
|
||||
Bag::Union(lh, rh) => {
|
||||
let l = lh.execute();
|
||||
if let Ok(mut l) = l {
|
||||
let r = rh.execute();
|
||||
if let Ok(mut r) = r {
|
||||
if rh.predict() < lh.predict() {
|
||||
l.append(&mut r);
|
||||
Ok(l)
|
||||
} else {
|
||||
r.append(&mut l);
|
||||
Ok(r)
|
||||
}
|
||||
} else {
|
||||
r
|
||||
}
|
||||
} else {
|
||||
l
|
||||
}
|
||||
}
|
||||
Bag::Bag(bags) => {
|
||||
let mut v = vec![];
|
||||
for bag in bags {
|
||||
let b = bag.execute();
|
||||
match b {
|
||||
e @ Err(_) => {
|
||||
return e;
|
||||
}
|
||||
Ok(mut b) => {
|
||||
//TODO: SPACE CONVERSIONS IF NOT THE SAME SPACES?
|
||||
v.append(&mut b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(v)
|
||||
}
|
||||
Bag::Inside(shape) => match shape {
|
||||
Shape::Point(space_id, position) => database::get_by_position(space_id, position),
|
||||
Shape::HyperRectangle(space_id, bounding_box) => {
|
||||
database::get_by_bounding_box(space_id, bounding_box)
|
||||
}
|
||||
Shape::HyperSphere(space_id, position, radius) => {
|
||||
let length = match radius {
|
||||
LiteralNumber::Int(x) => *x as f64,
|
||||
LiteralNumber::Float(x) => *x,
|
||||
};
|
||||
|
||||
match get_bounding_box(position, radius) {
|
||||
Err(e) => Err(e),
|
||||
Ok(inside) => match database::get_by_bounding_box(space_id, &inside) {
|
||||
e @ Err(_) => e,
|
||||
Ok(source) => {
|
||||
let mut filtered = vec![];
|
||||
|
||||
for point in source {
|
||||
// Include the surface of the sphere
|
||||
if point.length() <= length {
|
||||
filtered.push(point);
|
||||
}
|
||||
}
|
||||
Ok(filtered)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
Shape::Nifti(_space_id) => Err("not yet implemented".to_string()),
|
||||
},
|
||||
Bag::Union(lh, rh) => union(db, core_id, rh, lh, output_space, threshold_volume),
|
||||
Bag::Bag(list) => bag(db, core_id, list, output_space, threshold_volume),
|
||||
Bag::Inside(shape) => inside(db, core, shape, output_space, threshold),
|
||||
Bag::Outside(shape) => {
|
||||
fn outside_set(space_id: &String, inside: Vec<database::Object>) -> ResultSet {
|
||||
let mut outside = Vec::new();
|
||||
match database::get_all(space_id) {
|
||||
e @ Err(_) => e,
|
||||
Ok(points) => {
|
||||
for point in points {
|
||||
if !inside.contains(&point) {
|
||||
outside.push(point)
|
||||
}
|
||||
}
|
||||
|
||||
Ok(outside)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match shape {
|
||||
Shape::Point(space_id, position) => {
|
||||
match database::get_by_position(space_id, position) {
|
||||
e @ Err(_) => e,
|
||||
Ok(inside) => outside_set(space_id, inside),
|
||||
}
|
||||
}
|
||||
Shape::HyperRectangle(space_id, bounding_box) => {
|
||||
// We need to adapt the bounding_box to ensure the
|
||||
// surface will not hit as part of the inside set, so we
|
||||
// compute the biggest bounding box contained within the
|
||||
// given box.
|
||||
|
||||
// Smallest increment possible
|
||||
let mut low: Vec<LiteralNumber> = vec![];
|
||||
let LiteralPosition(coordinates) = &bounding_box[0];
|
||||
for coordinate in coordinates {
|
||||
match coordinate {
|
||||
LiteralNumber::Int(x) => low.push(LiteralNumber::Int(x + 1)),
|
||||
LiteralNumber::Float(x) => {
|
||||
low.push(LiteralNumber::Float(x + std::f64::EPSILON))
|
||||
}
|
||||
};
|
||||
}
|
||||
let low = LiteralPosition(low);
|
||||
|
||||
// Smallest decrement possible
|
||||
let mut high: Vec<LiteralNumber> = vec![];
|
||||
let LiteralPosition(coordinates) = &bounding_box[1];
|
||||
for coordinate in coordinates {
|
||||
match coordinate {
|
||||
LiteralNumber::Int(x) => high.push(LiteralNumber::Int(x - 1)),
|
||||
LiteralNumber::Float(x) => {
|
||||
high.push(LiteralNumber::Float(x - std::f64::EPSILON))
|
||||
}
|
||||
};
|
||||
}
|
||||
let high = LiteralPosition(high);
|
||||
|
||||
match database::get_by_bounding_box(space_id, &vec![low, high]) {
|
||||
e @ Err(_) => e,
|
||||
Ok(inside) => outside_set(space_id, inside),
|
||||
}
|
||||
}
|
||||
Shape::HyperSphere(space_id, position, radius) => {
|
||||
let length = match radius {
|
||||
LiteralNumber::Int(x) => *x as f64,
|
||||
LiteralNumber::Float(x) => *x,
|
||||
};
|
||||
|
||||
match get_bounding_box(position, radius) {
|
||||
Err(e) => Err(e),
|
||||
Ok(inside) => match database::get_by_bounding_box(space_id, &inside) {
|
||||
Err(e) => Err(e),
|
||||
Ok(source) => {
|
||||
let mut filtered = vec![];
|
||||
|
||||
for point in source {
|
||||
// Exclude the surface of the sphere, so
|
||||
// that it is included in the
|
||||
// complement.
|
||||
if point.length() < length {
|
||||
filtered.push(point);
|
||||
}
|
||||
}
|
||||
|
||||
outside_set(space_id, filtered)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
Shape::Nifti(_space_id) => Err("not yet implemented".to_string()),
|
||||
}
|
||||
//FIXME: This is currently computed as the complement of the values within the shape, except its surface.
|
||||
// Should this be instead a list of positions within the shape?
|
||||
//FIXME: Should we use the Shape's Space to get the maximum bounds or the output Space requested?
|
||||
outside(db, core, shape, output_space, threshold)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Predicate {
|
||||
pub fn eval(&self, point: &database::Object) -> bool {
|
||||
match self {
|
||||
Predicate::Not(predicate) => !predicate.eval(point),
|
||||
Predicate::And(lh, rh) => lh.eval(point) && rh.eval(point),
|
||||
Predicate::Or(lh, rh) => lh.eval(point) || rh.eval(point),
|
||||
// I don't know how to evaluate these at this point, so let the DB object take care of that.
|
||||
// Predicate::Less(selector, literal) => &selector.eval(point) < literal,
|
||||
// Predicate::Greater(selector, literal) => &selector.eval(point) > literal,
|
||||
// Predicate::Equal(selector, literal) => &selector.eval(point) == literal,
|
||||
// Redirect to the DB Objet the evaluation of the remaining predicate operators
|
||||
predicate => point.eval(predicate),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
pub trait Validator {
|
||||
type ValidationResult;
|
||||
|
||||
fn validate(&self) -> Self::ValidationResult;
|
||||
}
|
||||
|
||||
pub trait Predictor {
|
||||
fn predict(&self) -> f64;
|
||||
}
|
||||
|
||||
pub trait Executor {
|
||||
type ResultSet;
|
||||
|
||||
fn execute(&self) -> Self::ResultSet;
|
||||
}
|
||||
28
src/expressions.rs
Normal file
28
src/expressions.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
use mercator_db::DataBase;
|
||||
use mercator_db::SpaceObject;
|
||||
|
||||
pub trait Validator {
|
||||
type ValidationResult;
|
||||
|
||||
fn validate(&self) -> Self::ValidationResult;
|
||||
}
|
||||
|
||||
pub trait Predictor {
|
||||
fn predict(&self, db: &DataBase) -> Result<f64, String>;
|
||||
}
|
||||
|
||||
pub trait Executor {
|
||||
type ResultSet;
|
||||
|
||||
fn execute(
|
||||
&self,
|
||||
db: &DataBase,
|
||||
core_id: &str,
|
||||
output_space: Option<&str>,
|
||||
threshold_volume: Option<f64>,
|
||||
) -> Self::ResultSet;
|
||||
}
|
||||
|
||||
pub trait Evaluator {
|
||||
fn eval(&self, object: &SpaceObject) -> bool;
|
||||
}
|
||||
16
src/lib.rs
16
src/lib.rs
@@ -1,25 +1,23 @@
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
#[macro_use]
|
||||
extern crate lalrpop_util;
|
||||
|
||||
lalrpop_mod!(pub queries); // synthesized by LALRPOP
|
||||
lalrpop_mod!(#[allow(clippy::all)] pub queries); // synthesized by LALRPOP
|
||||
|
||||
mod database;
|
||||
mod evaluators;
|
||||
mod executors;
|
||||
mod expression;
|
||||
mod expressions;
|
||||
mod predictors;
|
||||
mod validators;
|
||||
|
||||
mod symbols;
|
||||
mod types;
|
||||
|
||||
pub use expression::Executor;
|
||||
pub use expression::Predictor;
|
||||
pub use expression::Validator;
|
||||
pub use expressions::Executor;
|
||||
pub use expressions::Predictor;
|
||||
pub use expressions::Validator;
|
||||
pub use queries::FiltersParser;
|
||||
pub use queries::QueryParser;
|
||||
pub use validators::ValidationResult;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
107
src/main.rs
107
src/main.rs
@@ -1,12 +1,16 @@
|
||||
#[macro_use]
|
||||
extern crate measure_time;
|
||||
|
||||
extern crate parser;
|
||||
|
||||
use parser::QueryParser;
|
||||
use parser::{Executor, Predictor, Validator};
|
||||
|
||||
use std::io;
|
||||
use std::process::exit;
|
||||
|
||||
use mercator_db::json::storage;
|
||||
use mercator_db::DataBase;
|
||||
use parser::Executor;
|
||||
use parser::FiltersParser;
|
||||
use parser::Predictor;
|
||||
use parser::QueryParser;
|
||||
use parser::Validator;
|
||||
|
||||
fn main() {
|
||||
// If RUST_LOG is unset, set it to INFO, otherwise keep it as-is.
|
||||
@@ -15,8 +19,41 @@ fn main() {
|
||||
}
|
||||
pretty_env_logger::init();
|
||||
|
||||
//let parser = queries::FiltersParser::new();
|
||||
let import;
|
||||
|
||||
if std::env::var("MERCATOR_IMPORT_DATA").is_err() {
|
||||
std::env::set_var("MERCATOR_IMPORT_DATA", "test");
|
||||
}
|
||||
|
||||
match std::env::var("MERCATOR_IMPORT_DATA") {
|
||||
Ok(val) => import = val,
|
||||
Err(val) => {
|
||||
error!("Could not fetch {} : `{}`", "MERCATOR_IMPORT_DATA", val);
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
// Convert to binary the JSON data:
|
||||
if true {
|
||||
info_time!("Converting to binary JSON data");
|
||||
storage::convert(&import);
|
||||
}
|
||||
|
||||
// Build a Database Index:
|
||||
if true {
|
||||
info_time!("Building database index");
|
||||
storage::build(&import);
|
||||
}
|
||||
|
||||
// Load a Database:
|
||||
let db;
|
||||
{
|
||||
info_time!("Loading database index");
|
||||
db = DataBase::load(import).unwrap();
|
||||
}
|
||||
|
||||
let parser = QueryParser::new();
|
||||
let parser = FiltersParser::new();
|
||||
|
||||
loop {
|
||||
println!();
|
||||
@@ -27,49 +64,59 @@ fn main() {
|
||||
Ok(0) => break, // Catch ^D
|
||||
Ok(1) => continue, // Catch \n
|
||||
Err(_) => continue,
|
||||
Ok(_) => (),
|
||||
}
|
||||
Ok(_) => {
|
||||
if input.trim().eq_ignore_ascii_case("quit") {
|
||||
break;
|
||||
}
|
||||
|
||||
if input.trim().eq_ignore_ascii_case("quit") {
|
||||
break;
|
||||
}
|
||||
info_time!("Interpretation");
|
||||
let mut parse;
|
||||
{
|
||||
info_time!("Parsing");
|
||||
parse = parser.parse(&input);
|
||||
}
|
||||
|
||||
let input = input.as_str();
|
||||
{
|
||||
debug_time!("Interpretation");
|
||||
let mut parse;
|
||||
{
|
||||
trace_time!("Parsing");
|
||||
parse = parser.parse(input);
|
||||
}
|
||||
trace!("Tree: \n{:?}", parse);
|
||||
if let Err(e) = &parse {
|
||||
warn!("Parsing failed: \n{:?}", e);
|
||||
} else {
|
||||
trace!("Tree: \n{:?}", parse);
|
||||
}
|
||||
|
||||
match parse {
|
||||
Ok(Some(t)) => {
|
||||
// QueryParser
|
||||
//if let Ok(Some(t)) = parse {
|
||||
|
||||
// FiltersParser
|
||||
if let Ok(t) = parse {
|
||||
let validate;
|
||||
{
|
||||
trace_time!("Type check");
|
||||
info_time!("Type check");
|
||||
validate = t.validate();
|
||||
}
|
||||
info!("Type: \n{:?}", validate);
|
||||
|
||||
if let Ok(_) = validate {
|
||||
if validate.is_ok() {
|
||||
let predict;
|
||||
{
|
||||
trace_time!("Prediction");
|
||||
predict = t.predict();
|
||||
info_time!("Prediction");
|
||||
predict = t.predict(&db);
|
||||
}
|
||||
info!("Predict: \n{:?}", predict);
|
||||
|
||||
let execute;
|
||||
{
|
||||
trace_time!("Exectution");
|
||||
execute = t.execute();
|
||||
info_time!("Execution");
|
||||
execute = t.execute(&db, "test", None, None);
|
||||
}
|
||||
|
||||
if let Ok(r) = execute {
|
||||
//let r = model::to_spatial_objects(&db, r);
|
||||
info!("Execution: \n{:#?}", r);
|
||||
info!("NB results: {:?}", r.len());
|
||||
} else {
|
||||
info!("Execution: \n{:?}", execute);
|
||||
}
|
||||
info!("Execution: \n{:?}", execute);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,48 +1,48 @@
|
||||
use super::expression::Predictor;
|
||||
use mercator_db::DataBase;
|
||||
|
||||
use super::expressions::Predictor;
|
||||
use super::symbols::*;
|
||||
|
||||
use super::database::space;
|
||||
|
||||
impl Predictor for Projection {
|
||||
fn predict(&self) -> f64 {
|
||||
fn predict(&self, db: &DataBase) -> Result<f64, String> {
|
||||
match self {
|
||||
Projection::Nifti(_, _, bag) => bag.predict(),
|
||||
Projection::JSON(_, _, bag) => bag.predict(),
|
||||
Projection::Nifti(_, _, bag) => bag.predict(db),
|
||||
Projection::JSON(_, _, bag) => bag.predict(db),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Predictor for Bag {
|
||||
fn predict(&self) -> f64 {
|
||||
fn predict(&self, db: &DataBase) -> Result<f64, String> {
|
||||
match self {
|
||||
Bag::Distinct(bag) => bag.predict(),
|
||||
Bag::Filter(_, bag) => bag.predict(),
|
||||
Bag::Complement(bag) => space::max_volume(bag.space()) - bag.predict(),
|
||||
Bag::Distinct(bag) => bag.predict(db),
|
||||
Bag::Filter(_, bag) => bag.predict(db),
|
||||
Bag::Complement(bag) => Ok(db.space(bag.space())?.volume() - bag.predict(db)?),
|
||||
Bag::Intersection(lh, rh) => {
|
||||
let l = lh.predict();
|
||||
let r = rh.predict();
|
||||
let l = lh.predict(db)?;
|
||||
let r = rh.predict(db)?;
|
||||
if l < r {
|
||||
l
|
||||
Ok(l)
|
||||
} else {
|
||||
r
|
||||
Ok(r)
|
||||
}
|
||||
}
|
||||
Bag::Union(lh, rh) => lh.predict() + rh.predict(),
|
||||
Bag::Union(lh, rh) => Ok(lh.predict(db)? + rh.predict(db)?),
|
||||
Bag::Bag(bags) => {
|
||||
let mut s = 0.0;
|
||||
for bag in bags {
|
||||
s += bag.predict();
|
||||
s += bag.predict(db)?;
|
||||
}
|
||||
s
|
||||
Ok(s)
|
||||
}
|
||||
Bag::Inside(shape) => shape.predict(),
|
||||
Bag::Outside(shape) => space::max_volume(shape.space()) - shape.predict(),
|
||||
Bag::Inside(shape) => shape.predict(db),
|
||||
Bag::Outside(shape) => Ok(db.space(shape.space())?.volume() - shape.predict(db)?),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Predictor for Shape {
|
||||
fn predict(&self) -> f64 {
|
||||
self.volume()
|
||||
fn predict(&self, _db: &DataBase) -> Result<f64, String> {
|
||||
Ok(self.volume())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use mercator_db::space::Space;
|
||||
|
||||
use crate::symbols;
|
||||
use crate::database::space;
|
||||
|
||||
grammar;
|
||||
|
||||
@@ -27,7 +28,7 @@ NiftiOperator: symbols::Projection = {
|
||||
")" => {
|
||||
let space_id = match rs {
|
||||
Some(id) => id,
|
||||
None => space::name().clone(),
|
||||
None => Space::universe().name().clone(),
|
||||
};
|
||||
|
||||
if let Some((sel, _)) = s {
|
||||
@@ -47,7 +48,7 @@ JsonOperator: symbols::Projection = {
|
||||
")" => {
|
||||
let space_id = match rs {
|
||||
Some(id) => id,
|
||||
None => space::name().clone(),
|
||||
None => Space::universe().name().clone(),
|
||||
};
|
||||
|
||||
symbols::Projection::JSON(space_id, f, b)
|
||||
@@ -148,9 +149,6 @@ Bags: symbols::Bag = {
|
||||
// Spatial Operators
|
||||
Inside,
|
||||
Outside,
|
||||
// When used directly here, the inside() operation on the shape is
|
||||
// implied.
|
||||
Shapes => symbols::Bag::Inside(<>)
|
||||
};
|
||||
|
||||
//*********************************************************************/
|
||||
@@ -186,26 +184,8 @@ Filter: symbols::Bag = {
|
||||
// "filter" "(" <p:Predicates> "," <b:Bags> ")" =>
|
||||
"filter" "(" <b:Bags> ")" =>
|
||||
symbols::Bag::Filter(None, Box::new(b)),
|
||||
"filter" "(" <p:Predicates> <b:("," <Bags> )?> ")" => match b {
|
||||
Some(b) => symbols::Bag::Filter(Some(p), Box::new(b)),
|
||||
None => {
|
||||
let v = space::bounding_box(space::name());
|
||||
let mut positions = Vec::new();
|
||||
for point in v {
|
||||
let mut position = Vec::new();
|
||||
for c in point {
|
||||
position.push(symbols::LiteralNumber::Int(*c))
|
||||
}
|
||||
positions.push(symbols::LiteralPosition(position));
|
||||
}
|
||||
let bb = symbols::Shape::HyperRectangle(
|
||||
space::name().clone(),
|
||||
positions
|
||||
);
|
||||
|
||||
symbols::Bag::Filter(Some(p), Box::new(symbols::Bag::Inside(bb)))
|
||||
}
|
||||
}
|
||||
"filter" "(" <p:Predicates> <b:("," <Bags> )?> ")" =>
|
||||
symbols::get_filter(p, b)
|
||||
};
|
||||
|
||||
Predicates: symbols::Predicate = {
|
||||
@@ -311,7 +291,7 @@ HyperRectangle: symbols::Shape = {
|
||||
"}" => {
|
||||
let space_id = match rs {
|
||||
Some(id) => id,
|
||||
None => space::name().clone(),
|
||||
None => Space::universe().name().clone(),
|
||||
};
|
||||
let mut pos = vec![l, h];
|
||||
for (_, lh, _, rh) in list.iter() {
|
||||
@@ -331,7 +311,7 @@ HyperSphere: symbols::Shape = {
|
||||
"}" => {
|
||||
let space_id = match rs {
|
||||
Some(id) => id,
|
||||
None => space::name().clone(),
|
||||
None => Space::universe().name().clone(),
|
||||
};
|
||||
|
||||
symbols::Shape::HyperSphere(space_id, c, r)
|
||||
@@ -342,7 +322,7 @@ Point: symbols::Shape = {
|
||||
"point" "{" <pos:Position> <rs:( "," <String> )?> "}" => {
|
||||
let space_id = match rs {
|
||||
Some(id) => id,
|
||||
None => space::name().clone(),
|
||||
None => Space::universe().name().clone(),
|
||||
};
|
||||
|
||||
symbols::Shape::Point(space_id, pos)
|
||||
@@ -365,7 +345,7 @@ Nifti: symbols::Shape = {
|
||||
"}" => {
|
||||
let space_id = match rs {
|
||||
Some(id) => id,
|
||||
None => space::name().clone(),
|
||||
None => Space::universe().name().clone(),
|
||||
};
|
||||
symbols::Shape::Nifti(space_id)
|
||||
}
|
||||
@@ -383,7 +363,6 @@ ByteProvider = { "uri" "(" String ")" };
|
||||
// Always returns a vector of numbers, a.k.a a position (a scalar will
|
||||
// be represented as a vector of one element)
|
||||
Positions: symbols::Position = {
|
||||
StrCmpICase,
|
||||
StrCmp,
|
||||
Selector => symbols::Position::Selector(<>),
|
||||
Position => symbols::Position::Literal(<>)
|
||||
@@ -399,13 +378,6 @@ StrCmp: symbols::Position = {
|
||||
}
|
||||
};
|
||||
|
||||
// Same, but case insensitive.
|
||||
StrCmpICase: symbols::Position = {
|
||||
"str_cmp_ignore_case" "(" <s:Selector> "," <v:String> ")" => {
|
||||
symbols::Position::StrCmpICase(s, v)
|
||||
}
|
||||
};
|
||||
|
||||
// FIXME: FIELDS are expected to be exisiting in the data model. Root Object is assumed to be the type of the ressource on which the POST call was done.
|
||||
Selector: symbols::LiteralSelector = {
|
||||
( <Field> )+ => symbols::LiteralSelector(<>)
|
||||
|
||||
152
src/symbols.rs
152
src/symbols.rs
@@ -1,6 +1,8 @@
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use super::database::space;
|
||||
use mercator_db::space;
|
||||
use mercator_db::SpaceObject;
|
||||
|
||||
pub use super::types::*;
|
||||
|
||||
/**********************************************************************/
|
||||
@@ -63,6 +65,8 @@ pub enum Bag {
|
||||
Bag(Vec<Bag>),
|
||||
Inside(Shape),
|
||||
Outside(Shape),
|
||||
//FIXME: ADD A SHAPE VARIANT WHICH JUST RETURNS ALL THE POSITIONS OF THAT SHAPE
|
||||
//Shape(Shape),
|
||||
}
|
||||
|
||||
impl Bag {
|
||||
@@ -84,7 +88,7 @@ impl Bag {
|
||||
Bag::Bag(_) => {
|
||||
// Bags can be defined in different spaces, thus the output is
|
||||
// always in the universe space.
|
||||
space::name()
|
||||
space::Space::universe().name()
|
||||
}
|
||||
Bag::Inside(shape) => shape.space(),
|
||||
Bag::Outside(shape) => shape.space(),
|
||||
@@ -163,12 +167,12 @@ impl Shape {
|
||||
|
||||
volume
|
||||
}
|
||||
Shape::HyperSphere(_space, pos, r) => {
|
||||
Shape::HyperSphere(_space, pos, radius) => {
|
||||
// Formula from https://en.wikipedia.org/wiki/N-sphere#/media/File:N_SpheresVolumeAndSurfaceArea.png
|
||||
let LiteralPosition(p) = pos;
|
||||
let k = p.len(); // Number of dimensions.
|
||||
let LiteralPosition(position) = pos;
|
||||
let k = position.len(); // Number of dimensions.
|
||||
|
||||
let r = match *r {
|
||||
let radius = match *radius {
|
||||
LiteralNumber::Int(x) => x as f64,
|
||||
LiteralNumber::Float(x) => x,
|
||||
};
|
||||
@@ -178,12 +182,12 @@ impl Shape {
|
||||
|
||||
// Set starting values for the coefficient
|
||||
let mut a = 2.0;
|
||||
let mut i = 1;
|
||||
|
||||
if (k % 2) == 0 {
|
||||
let mut i = if (k % 2) == 0 {
|
||||
a = pi;
|
||||
i = 2;
|
||||
}
|
||||
2
|
||||
} else {
|
||||
1
|
||||
};
|
||||
|
||||
while i < k {
|
||||
i += 2;
|
||||
@@ -191,7 +195,7 @@ impl Shape {
|
||||
a /= i as f64;
|
||||
}
|
||||
|
||||
a * r.powi(i as i32)
|
||||
a * radius.powi(i as i32)
|
||||
}
|
||||
Shape::Nifti(_) => unimplemented!(),
|
||||
}
|
||||
@@ -203,12 +207,28 @@ impl Shape {
|
||||
/**********************************************************************/
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Position {
|
||||
StrCmpICase(LiteralSelector, String),
|
||||
StrCmp(LiteralSelector, String),
|
||||
Selector(LiteralSelector),
|
||||
Literal(LiteralPosition),
|
||||
}
|
||||
|
||||
impl Position {
|
||||
pub fn value(&self, object: &SpaceObject) -> LiteralPosition {
|
||||
match self {
|
||||
Position::Literal(literal) => literal.clone(),
|
||||
Position::Selector(selector) => selector.position(object),
|
||||
Position::StrCmp(selector, literal) => {
|
||||
let x = match (selector.str(object)).cmp(literal) {
|
||||
Ordering::Equal => 0,
|
||||
Ordering::Greater => 1,
|
||||
Ordering::Less => -1,
|
||||
};
|
||||
LiteralPosition(vec![LiteralNumber::Int(x)])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**********************************************************************/
|
||||
/* Literals / TOKENS */
|
||||
/**********************************************************************/
|
||||
@@ -222,6 +242,17 @@ pub enum LiteralNumber {
|
||||
Float(f64),
|
||||
}
|
||||
|
||||
impl From<&LiteralNumber> for Vec<f64> {
|
||||
fn from(l: &LiteralNumber) -> Self {
|
||||
let r = match l {
|
||||
LiteralNumber::Int(x) => (*x) as f64,
|
||||
LiteralNumber::Float(x) => *x,
|
||||
};
|
||||
|
||||
vec![r]
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for LiteralNumber {
|
||||
fn eq(&self, other: &LiteralNumber) -> bool {
|
||||
match self {
|
||||
@@ -270,6 +301,36 @@ impl LiteralPosition {
|
||||
|
||||
a
|
||||
}
|
||||
|
||||
pub fn dimensions(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&LiteralNumber> for f64 {
|
||||
fn from(l: &LiteralNumber) -> Self {
|
||||
match l {
|
||||
LiteralNumber::Int(x) => (*x) as f64,
|
||||
LiteralNumber::Float(x) => *x,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&LiteralPosition> for Vec<f64> {
|
||||
fn from(l: &LiteralPosition) -> Self {
|
||||
let LiteralPosition(v) = l;
|
||||
let mut r = Vec::with_capacity(v.len());
|
||||
|
||||
for x in v {
|
||||
let x = match x {
|
||||
LiteralNumber::Int(x) => (*x) as f64,
|
||||
LiteralNumber::Float(x) => *x,
|
||||
};
|
||||
r.push(x);
|
||||
}
|
||||
|
||||
r
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for LiteralPosition {
|
||||
@@ -316,4 +377,69 @@ impl LiteralSelector {
|
||||
// FIXME: Pretend for now that everything is a number, needs to be actually looked up in data model.
|
||||
LiteralTypes::Int
|
||||
}
|
||||
|
||||
// FIXME: THIS IS SOOO WRONG
|
||||
pub fn position(&self, object: &SpaceObject) -> LiteralPosition {
|
||||
println!("LiteralSelector.position(): {:?}", self);
|
||||
let v: Vec<f64> = object.position.clone().into();
|
||||
|
||||
LiteralPosition(v.into_iter().map(LiteralNumber::Float).collect::<Vec<_>>())
|
||||
}
|
||||
|
||||
// FIXME: THIS IS SOOO WRONG
|
||||
pub fn str(&self, object: &SpaceObject) -> String {
|
||||
let LiteralSelector(v) = self;
|
||||
let last = v.last();
|
||||
if let Some(Field(name, _)) = last {
|
||||
if name == "id" {
|
||||
return object.value.id().clone();
|
||||
} else if name == "type" {
|
||||
return object.value.type_name();
|
||||
} else if name == "reference_space" {
|
||||
return object.space_id.clone();
|
||||
}
|
||||
}
|
||||
|
||||
println!("LiteralSelector.str(): {:?}", self);
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
// The logic was getting a bit too complex to be embedded directly into the
|
||||
// grammar definition.
|
||||
pub fn get_filter(p: Predicate, b: Option<Bag>) -> Bag {
|
||||
match b {
|
||||
Some(b) => Bag::Filter(Some(p), Box::new(b)),
|
||||
None => {
|
||||
let (low, high) = space::Space::universe().bounding_box();
|
||||
let low: Vec<_> = low.into();
|
||||
let high: Vec<_> = high.into();
|
||||
let bb = Shape::HyperRectangle(
|
||||
space::Space::universe().name().clone(),
|
||||
vec![
|
||||
LiteralPosition(
|
||||
low.into_iter()
|
||||
.map(LiteralNumber::Float)
|
||||
.collect::<Vec<_>>(),
|
||||
),
|
||||
LiteralPosition(
|
||||
high.into_iter()
|
||||
.map(LiteralNumber::Float)
|
||||
.collect::<Vec<_>>(),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
Bag::Filter(Some(p), Box::new(Bag::Inside(bb)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//FIXME: HACK FOR NOW; NEED PROPER TYPE CHECKING!
|
||||
pub fn get_type() -> LiteralTypes {
|
||||
LiteralTypes::Vector(vec![
|
||||
LiteralTypes::Float,
|
||||
LiteralTypes::Float,
|
||||
LiteralTypes::Float,
|
||||
])
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use super::database::space;
|
||||
use super::expression::Validator;
|
||||
use super::expressions::Validator;
|
||||
use super::symbols::*;
|
||||
|
||||
pub type ValidationResult = Result<LiteralTypes, String>;
|
||||
@@ -24,7 +23,7 @@ impl Validator for Bag {
|
||||
type ValidationResult = self::ValidationResult;
|
||||
|
||||
fn validate(&self) -> ValidationResult {
|
||||
fn compare_bag_types(lh: &Box<Bag>, rh: &Box<Bag>) -> ValidationResult {
|
||||
fn compare_bag_types(lh: &Bag, rh: &Bag) -> ValidationResult {
|
||||
if lh.space().cmp(rh.space()) != std::cmp::Ordering::Equal {
|
||||
return Err(format!(
|
||||
"left and right sets are defined in different reference spaces: '{}' vs '{}'.",
|
||||
@@ -68,7 +67,7 @@ impl Validator for Bag {
|
||||
}
|
||||
}
|
||||
|
||||
Ok(space::get_type(space::name()).clone())
|
||||
Ok(get_type())
|
||||
}
|
||||
Bag::Inside(shape) => shape.validate(),
|
||||
Bag::Outside(shape) => shape.validate(),
|
||||
|
||||
Reference in New Issue
Block a user