use std::collections::HashSet; use mercator_db::space; use mercator_db::Core; use mercator_db::CoreQueryParameters; use mercator_db::SpaceObject; use super::expressions::*; use super::symbols::*; impl From<&LiteralPosition> for space::Position { fn from(literal: &LiteralPosition) -> Self { let v: Vec = 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( core: &Core, parameters: &CoreQueryParameters, space_id: &str, inside: Vec, ) -> mercator_db::ResultSet { 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) => Ok(points .into_iter() .filter(|o| !inside.contains(&o)) .collect::>()), } } fn distinct(core_id: &str, parameters: &CoreQueryParameters, bag: &Bag) -> mercator_db::ResultSet { 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( core_id: &str, parameters: &CoreQueryParameters, predicate: &Option, bag: &Bag, ) -> mercator_db::ResultSet { 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(|o| predicate.eval(&o)) .collect::>()), }, } } fn complement( core_id: &str, parameters: &CoreQueryParameters, core: &Core, bag: &Bag, ) -> mercator_db::ResultSet { 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( core_id: &str, parameters: &CoreQueryParameters, rh: &Bag, lh: &Bag, ) -> mercator_db::ResultSet { 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( core_id: &str, parameters: &CoreQueryParameters, rh: &Bag, lh: &Bag, ) -> mercator_db::ResultSet { 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(core_id: &str, parameters: &CoreQueryParameters, bags: &[Bag]) -> mercator_db::ResultSet { 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(parameters: &CoreQueryParameters, core: &Core, shape: &Shape) -> mercator_db::ResultSet { let db = parameters.db; let param = match shape { Shape::Point(space_id, position) => { let space = db.space(space_id)?; let position: Vec = 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 = (&bounding_box[0]).into(); let high: Vec = (&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 = 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(parameters: &CoreQueryParameters, core: &Core, shape: &Shape) -> mercator_db::ResultSet { match shape { Shape::Point(space_id, position) => { let position: Vec = 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 Executor for Projection { type ResultSet = mercator_db::ResultSet; fn execute(&self, core_id: &str, parameters: &CoreQueryParameters) -> 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 Executor for Bag { type ResultSet = mercator_db::ResultSet; fn execute(&self, core_id: &str, parameters: &CoreQueryParameters) -> Self::ResultSet { let core = parameters.db.core(core_id)?; match self { 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) } } } }