Files
mercator_parser/src/executors.rs

363 lines
11 KiB
Rust

use std::collections::{HashMap, HashSet};
use mercator_db::space;
use mercator_db::Core;
use mercator_db::CoreQueryParameters;
use mercator_db::Properties;
use super::expressions::*;
use super::symbols::*;
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<'c>(
core: &'c Core,
parameters: &CoreQueryParameters<'c>,
space_id: &str,
inside: Vec<(&'c String, Vec<(space::Position, &'c Properties)>)>,
) -> mercator_db::ResultSet<'c> {
let (low, high) = parameters.db.space(space_id)?.bounding_box();
match core.get_by_shape(parameters, &space::Shape::BoundingBox(low, high), space_id) {
e @ Err(_) => e,
Ok(points) => {
let hashmap = inside.into_iter().collect::<HashMap<_, _>>();
Ok(points
.into_iter()
.filter_map(|(space, v)| match hashmap.get(space) {
None => None,
Some(list) => {
Some((space, v.into_iter().filter(|t| !list.contains(t)).collect()))
}
})
.collect::<Vec<_>>())
}
}
}
fn view_port<'c>(
core_id: &str,
parameters: &CoreQueryParameters<'c>,
bag: &Bag,
) -> mercator_db::ResultSet<'c> {
if let Some((low, high)) = parameters.view_port {
let vp = Bag::Inside(Shape::HyperRectangle(
bag.space().clone(),
vec![low.into(), high.into()],
));
intersection(core_id, parameters, &vp, bag)
} else {
bag.execute(core_id, parameters)
}
}
fn distinct<'c>(
core_id: &str,
parameters: &CoreQueryParameters<'c>,
bag: &Bag,
) -> mercator_db::ResultSet<'c> {
match bag.execute(core_id, parameters) {
e @ Err(_) => e,
Ok(mut v) => {
let set: HashSet<_> = v.drain(..).collect(); // dedup
v.extend(set.into_iter());
Ok(v)
}
}
}
fn filter<'c>(
core_id: &str,
parameters: &CoreQueryParameters<'c>,
predicate: &Option<Predicate>,
bag: &Bag,
) -> mercator_db::ResultSet<'c> {
match predicate {
None => bag.execute(core_id, parameters),
Some(predicate) => match bag.execute(core_id, parameters) {
e @ Err(_) => e,
Ok(results) => Ok(results
.into_iter()
.filter_map(|(space, positions)| {
let filtered = positions
.into_iter()
.filter(|(position, properties)| {
predicate.eval((space, position, properties))
})
.collect::<Vec<_>>();
if filtered.is_empty() {
None
} else {
Some((space, filtered))
}
})
.collect::<Vec<_>>()),
},
}
}
fn complement<'c>(
core_id: &str,
parameters: &CoreQueryParameters<'c>,
core: &'c Core,
bag: &Bag,
) -> mercator_db::ResultSet<'c> {
match bag.execute(core_id, parameters) {
// FIXME: The complement of a set is computed within its definition space.
e @ Err(_) => e,
Ok(inside) => complement_helper(
core,
parameters,
mercator_db::space::Space::universe().name(),
inside,
),
}
}
fn intersection<'c>(
core_id: &str,
parameters: &CoreQueryParameters<'c>,
rh: &Bag,
lh: &Bag,
) -> mercator_db::ResultSet<'c> {
let l = lh.execute(core_id, parameters);
if let Ok(l) = l {
let r = rh.execute(core_id, parameters);
if let Ok(r) = r {
let mut v = vec![];
if rh.predict(parameters.db) < lh.predict(parameters.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<'c>(
core_id: &str,
parameters: &CoreQueryParameters<'c>,
rh: &Bag,
lh: &Bag,
) -> mercator_db::ResultSet<'c> {
let l = lh.execute(core_id, parameters);
if let Ok(mut l) = l {
let r = rh.execute(core_id, parameters);
if let Ok(mut r) = r {
if rh.predict(parameters.db) < lh.predict(parameters.db) {
l.append(&mut r);
Ok(l)
} else {
r.append(&mut l);
Ok(r)
}
} else {
r
}
} else {
l
}
}
fn bag<'c>(
core_id: &str,
parameters: &CoreQueryParameters<'c>,
bags: &[Bag],
) -> mercator_db::ResultSet<'c> {
let mut v = vec![];
for bag in bags {
let b = bag.execute(core_id, parameters);
match b {
e @ Err(_) => {
return e;
}
Ok(mut b) => {
v.append(&mut b);
}
}
}
Ok(v)
}
fn inside<'c>(
parameters: &CoreQueryParameters<'c>,
core: &'c Core,
shape: &Shape,
) -> mercator_db::ResultSet<'c> {
let db = parameters.db;
let param = 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 param {
Ok((space_id, shape)) => core.get_by_shape(parameters, &shape, space_id),
Err(e) => Err(e),
}
}
fn outside<'c>(
parameters: &CoreQueryParameters<'c>,
core: &'c Core,
shape: &Shape,
) -> mercator_db::ResultSet<'c> {
match shape {
Shape::Point(space_id, position) => {
let position: Vec<f64> = position.into();
match core.get_by_positions(parameters, &[position.into()], space_id) {
e @ Err(_) => e,
Ok(inside) => complement_helper(core, parameters, 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 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(parameters, &space::Shape::BoundingBox(low, high), space_id) {
e @ Err(_) => e,
Ok(inside) => complement_helper(core, parameters, space_id, inside),
}
}
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(
parameters,
&space::Shape::HyperSphere(center, radius.into()),
space_id,
) {
e @ Err(_) => e,
Ok(inside) => complement_helper(core, parameters, space_id, inside),
}
}
Shape::Nifti(_space_id) => Err("Outside-nifti: not yet implemented".to_string()),
}
}
impl<'e> Executor<'e> for Projection {
type ResultSet = mercator_db::ResultSet<'e>;
fn execute<'f: 'e>(
&self,
core_id: &str,
parameters: &CoreQueryParameters<'f>,
) -> Self::ResultSet {
match self {
Projection::Nifti(_, _, _bag) => Err("Proj-Nifti: not yet implemented".to_string()),
Projection::JSON(_, _format, bag) => {
bag.execute(core_id, parameters)
// FIXME: Add projections here
}
}
}
}
impl<'e> Executor<'e> for Bag {
type ResultSet = mercator_db::ResultSet<'e>;
fn execute<'f: 'e>(
&self,
core_id: &str,
parameters: &CoreQueryParameters<'f>,
) -> Self::ResultSet {
let core = parameters.db.core(core_id)?;
match self {
Bag::ViewPort(bag) => view_port(core_id, parameters, bag),
Bag::Distinct(bag) => distinct(core_id, parameters, bag),
Bag::Filter(predicate, bag) => filter(core_id, parameters, predicate, bag),
Bag::Complement(bag) => complement(core_id, parameters, core, bag),
Bag::Intersection(lh, rh) => intersection(core_id, parameters, rh, lh),
Bag::Union(lh, rh) => union(core_id, parameters, rh, lh),
Bag::Bag(list) => bag(core_id, parameters, list),
Bag::Inside(shape) => inside(parameters, core, shape),
Bag::Outside(shape) => {
//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(parameters, core, shape)
}
}
}
}