Sun, 27 Apr 2025 15:56:43 -0500
MinMaxMapping trait to allow alternatives to BTFN with relevant properties.
--- a/Cargo.lock Sun Apr 27 15:45:40 2025 -0500 +++ b/Cargo.lock Sun Apr 27 15:56:43 2025 -0500 @@ -4,7 +4,7 @@ [[package]] name = "alg_tools" -version = "0.3.1" +version = "0.4.0-dev" dependencies = [ "anyhow", "colored",
--- a/src/bisection_tree/aggregator.rs Sun Apr 27 15:45:40 2025 -0500 +++ b/src/bisection_tree/aggregator.rs Sun Apr 27 15:56:43 2025 -0500 @@ -2,8 +2,7 @@ Aggregation / summarisation of information in branches of bisection trees. */ -use crate::instance::Instance; -use crate::sets::Set; +pub use crate::bounds::Bounds; use crate::types::*; /// Trait for aggregating information about a branch of a [bisection tree][super::BT]. @@ -56,144 +55,6 @@ } } -/// Upper and lower bounds on an `F`-valued function. -#[derive(Copy, Clone, Debug)] -pub struct Bounds<F>( - /// Lower bound - pub F, - /// Upper bound - pub F, -); - -impl<F: Copy> Bounds<F> { - /// Returns the lower bound - #[inline] - pub fn lower(&self) -> F { - self.0 - } - - /// Returns the upper bound - #[inline] - pub fn upper(&self) -> F { - self.1 - } -} - -impl<F: Float> Bounds<F> { - /// Returns a uniform bound. - /// - /// This is maximum over the absolute values of the upper and lower bound. - #[inline] - pub fn uniform(&self) -> F { - let &Bounds(lower, upper) = self; - lower.abs().max(upper.abs()) - } - - /// Construct a bounds, making sure `lower` bound is less than `upper` - #[inline] - pub fn corrected(lower: F, upper: F) -> Self { - if lower <= upper { - Bounds(lower, upper) - } else { - Bounds(upper, lower) - } - } - - /// Refine the lower bound - #[inline] - pub fn refine_lower(&self, lower: F) -> Self { - let &Bounds(l, u) = self; - debug_assert!(l <= u); - Bounds(l.max(lower), u.max(lower)) - } - - /// Refine the lower bound - #[inline] - pub fn refine_upper(&self, upper: F) -> Self { - let &Bounds(l, u) = self; - debug_assert!(l <= u); - Bounds(l.min(upper), u.min(upper)) - } -} - -impl<'a, F: Float> std::ops::Add<Self> for Bounds<F> { - type Output = Self; - #[inline] - fn add(self, Bounds(l2, u2): Self) -> Self::Output { - let Bounds(l1, u1) = self; - debug_assert!(l1 <= u1 && l2 <= u2); - Bounds(l1 + l2, u1 + u2) - } -} - -impl<'a, F: Float> std::ops::Mul<Self> for Bounds<F> { - type Output = Self; - #[inline] - fn mul(self, Bounds(l2, u2): Self) -> Self::Output { - let Bounds(l1, u1) = self; - debug_assert!(l1 <= u1 && l2 <= u2); - let a = l1 * l2; - let b = u1 * u2; - // The order may flip when negative numbers are involved, so need min/max - Bounds(a.min(b), a.max(b)) - } -} - -impl<F: Float> std::iter::Product for Bounds<F> { - #[inline] - fn product<I>(mut iter: I) -> Self - where - I: Iterator<Item = Self>, - { - match iter.next() { - None => Bounds(F::ZERO, F::ZERO), - Some(init) => iter.fold(init, |a, b| a * b), - } - } -} - -impl<F: Float> Set<F> for Bounds<F> { - fn contains<I: Instance<F>>(&self, item: I) -> bool { - let v = item.own(); - let &Bounds(l, u) = self; - debug_assert!(l <= u); - l <= v && v <= u - } -} - -impl<F: Float> Bounds<F> { - /// Calculate a common bound (glb, lub) for two bounds. - #[inline] - pub fn common(&self, &Bounds(l2, u2): &Self) -> Self { - let &Bounds(l1, u1) = self; - debug_assert!(l1 <= u1 && l2 <= u2); - Bounds(l1.min(l2), u1.max(u2)) - } - - /// Indicates whether `Self` is a superset of the argument bound. - #[inline] - pub fn superset(&self, &Bounds(l2, u2): &Self) -> bool { - let &Bounds(l1, u1) = self; - debug_assert!(l1 <= u1 && l2 <= u2); - l1 <= l2 && u2 <= u1 - } - - /// Returns the greatest bound contained by both argument bounds, if one exists. - #[inline] - pub fn glb(&self, &Bounds(l2, u2): &Self) -> Option<Self> { - let &Bounds(l1, u1) = self; - debug_assert!(l1 <= u1 && l2 <= u2); - let l = l1.max(l2); - let u = u1.min(u2); - debug_assert!(l <= u); - if l < u { - Some(Bounds(l, u)) - } else { - None - } - } -} - impl<F: Float> Aggregator for Bounds<F> { #[inline] fn aggregate<I>(&mut self, aggregates: I)
--- a/src/bisection_tree/bt.rs Sun Apr 27 15:45:40 2025 -0500 +++ b/src/bisection_tree/bt.rs Sun Apr 27 15:56:43 2025 -0500 @@ -246,7 +246,10 @@ { /// Creates a new node branching structure, subdividing `domain` based on the /// [hint][Support::support_hint] of `support`. - pub(super) fn new_with<S: LocalAnalysis<F, A, N>>(domain: &Cube<F, N>, support: &S) -> Self { + pub(super) fn new_with<S: LocalAnalysis<F, A, N> + Support<F, N>>( + domain: &Cube<F, N>, + support: &S, + ) -> Self { let hint = support.bisection_hint(domain); let branch_at = map2(&hint, domain, |h, r| { h.unwrap_or_else(|| (r[0] + r[1]) / F::TWO) @@ -329,7 +332,7 @@ /// * `support` is the [`Support`] that is used determine with which subcubes of `domain` /// (at subdivision depth `new_leaf_depth`) the data `d` is to be associated with. /// - pub(super) fn insert<'refs, 'scope, M: Depth, S: LocalAnalysis<F, A, N>>( + pub(super) fn insert<'refs, 'scope, M: Depth, S: LocalAnalysis<F, A, N> + Support<F, N>>( &mut self, domain: &Cube<F, N>, d: D, @@ -456,7 +459,7 @@ /// If `self` is a [`NodeOption::Branches`], the data is passed to branches whose subcubes /// `support` intersects. If an [`NodeOption::Uninitialised`] node is encountered, a new leaf is /// created at a minimum depth of `new_leaf_depth`. - pub(super) fn insert<'refs, 'scope, M: Depth, S: LocalAnalysis<F, A, N>>( + pub(super) fn insert<'refs, 'scope, M: Depth, S: LocalAnalysis<F, A, N> + Support<F, N>>( &mut self, domain: &Cube<F, N>, d: D, @@ -626,7 +629,11 @@ /// /// Every leaf node of the tree that intersects the `support` will contain a copy of /// `d`. - fn insert<S: LocalAnalysis<F, Self::Agg, N>>(&mut self, d: Self::Data, support: &S); + fn insert<S: LocalAnalysis<F, Self::Agg, N> + Support<F, N>>( + &mut self, + d: Self::Data, + support: &S, + ); /// Construct a new instance of the tree for a different aggregator /// @@ -696,7 +703,7 @@ type Agg = A; type Converted<ANew> = BT<M,F,D,ANew,$n> where ANew : Aggregator; - fn insert<S: LocalAnalysis<F, A, $n>>( + fn insert<S: LocalAnalysis<F, A, $n> + Support<F, $n>>( &mut self, d : D, support : &S
--- a/src/bisection_tree/btfn.rs Sun Apr 27 15:45:40 2025 -0500 +++ b/src/bisection_tree/btfn.rs Sun Apr 27 15:56:43 2025 -0500 @@ -12,6 +12,7 @@ use super::either::*; use super::refine::*; use super::support::*; +use crate::bounds::MinMaxMapping; use crate::fe_model::base::RealLocalModel; use crate::fe_model::p2_local_model::*; use crate::loc::Loc; @@ -837,18 +838,14 @@ // there should be a result, or new nodes above the `glb` inserted into the queue. Then the waiting // threads can also continue processing. If, however, numerical inaccuracy destroyes the `glb`, // the queue may run out, and we get “Refiner failure”. -impl<F: Float, G, BT, const N: usize> BTFN<F, G, BT, N> +impl<F: Float, G, BT, const N: usize> MinMaxMapping<F, N> for BTFN<F, G, BT, N> where BT: BTSearch<F, N, Agg = Bounds<F>>, G: SupportGenerator<F, N, Id = BT::Data>, G::SupportType: Mapping<Loc<F, N>, Codomain = F> + LocalAnalysis<F, Bounds<F>, N>, Cube<F, N>: P2Minimise<Loc<F, N>, F>, { - /// Maximise the `BTFN` within stated value `tolerance`. - /// - /// At most `max_steps` refinement steps are taken. - /// Returns the approximate maximiser and the corresponding function value. - pub fn maximise(&mut self, tolerance: F, max_steps: usize) -> (Loc<F, N>, F) { + fn maximise(&mut self, tolerance: F, max_steps: usize) -> (Loc<F, N>, F) { let refiner = P2Refiner { tolerance, max_steps, @@ -861,12 +858,7 @@ .unwrap() } - /// Maximise the `BTFN` within stated value `tolerance` subject to a lower bound. - /// - /// At most `max_steps` refinement steps are taken. - /// Returns the approximate maximiser and the corresponding function value when one is found - /// above the `bound` threshold, otherwise `None`. - pub fn maximise_above( + fn maximise_above( &mut self, bound: F, tolerance: F, @@ -883,11 +875,7 @@ .expect("Refiner failure.") } - /// Minimise the `BTFN` within stated value `tolerance`. - /// - /// At most `max_steps` refinement steps are taken. - /// Returns the approximate minimiser and the corresponding function value. - pub fn minimise(&mut self, tolerance: F, max_steps: usize) -> (Loc<F, N>, F) { + fn minimise(&mut self, tolerance: F, max_steps: usize) -> (Loc<F, N>, F) { let refiner = P2Refiner { tolerance, max_steps, @@ -900,12 +888,7 @@ .unwrap() } - /// Minimise the `BTFN` within stated value `tolerance` subject to a lower bound. - /// - /// At most `max_steps` refinement steps are taken. - /// Returns the approximate minimiser and the corresponding function value when one is found - /// above the `bound` threshold, otherwise `None`. - pub fn minimise_below( + fn minimise_below( &mut self, bound: F, tolerance: F, @@ -922,10 +905,7 @@ .expect("Refiner failure.") } - /// Verify that the `BTFN` has a given upper `bound` within indicated `tolerance`. - /// - /// At most `max_steps` refinement steps are taken. - pub fn has_upper_bound(&mut self, bound: F, tolerance: F, max_steps: usize) -> bool { + fn has_upper_bound(&mut self, bound: F, tolerance: F, max_steps: usize) -> bool { let refiner = BoundRefiner { bound, tolerance, @@ -937,10 +917,7 @@ .expect("Refiner failure.") } - /// Verify that the `BTFN` has a given lower `bound` within indicated `tolerance`. - /// - /// At most `max_steps` refinement steps are taken. - pub fn has_lower_bound(&mut self, bound: F, tolerance: F, max_steps: usize) -> bool { + fn has_lower_bound(&mut self, bound: F, tolerance: F, max_steps: usize) -> bool { let refiner = BoundRefiner { bound, tolerance,
--- a/src/bisection_tree/support.rs Sun Apr 27 15:45:40 2025 -0500 +++ b/src/bisection_tree/support.rs Sun Apr 27 15:56:43 2025 -0500 @@ -2,6 +2,7 @@ Traits for representing the support of a [`Mapping`], and analysing the mapping on a [`Cube`]. */ use super::aggregator::Bounds; +pub use crate::bounds::{GlobalAnalysis, LocalAnalysis}; use crate::loc::Loc; use crate::mapping::{DifferentiableImpl, DifferentiableMapping, Instance, Mapping, Space}; use crate::maputil::map2; @@ -52,56 +53,6 @@ } } -/// Trait for globally analysing a property `A` of a [`Mapping`]. -/// -/// Typically `A` is an [`Aggregator`][super::aggregator::Aggregator] such as -/// [`Bounds`][super::aggregator::Bounds]. -pub trait GlobalAnalysis<F: Num, A> { - /// Perform global analysis of the property `A` of `Self`. - /// - /// As an example, in the case of `A` being [`Bounds`][super::aggregator::Bounds], - /// this function will return global upper and lower bounds for the mapping - /// represented by `self`. - fn global_analysis(&self) -> A; -} - -// default impl<F, A, N, L> GlobalAnalysis<F, A, N> for L -// where L : LocalAnalysis<F, A, N> { -// #[inline] -// fn global_analysis(&self) -> Bounds<F> { -// self.local_analysis(&self.support_hint()) -// } -// } - -/// Trait for locally analysing a property `A` of a [`Mapping`] (implementing [`Support`]) -/// within a [`Cube`]. -/// -/// Typically `A` is an [`Aggregator`][super::aggregator::Aggregator] such as -/// [`Bounds`][super::aggregator::Bounds]. -pub trait LocalAnalysis<F: Num, A, const N: usize>: GlobalAnalysis<F, A> + Support<F, N> { - /// Perform local analysis of the property `A` of `Self`. - /// - /// As an example, in the case of `A` being [`Bounds`][super::aggregator::Bounds], - /// this function will return upper and lower bounds within `cube` for the mapping - /// represented by `self`. - fn local_analysis(&self, cube: &Cube<F, N>) -> A; -} - -/// Trait for determining the upper and lower bounds of an float-valued [`Mapping`]. -/// -/// This is a blanket-implemented alias for [`GlobalAnalysis`]`<F, Bounds<F>>` -/// [`Mapping`] is not a supertrait to allow flexibility in the implementation of either -/// reference or non-reference arguments. -pub trait Bounded<F: Float>: GlobalAnalysis<F, Bounds<F>> { - /// Return lower and upper bounds for the values of of `self`. - #[inline] - fn bounds(&self) -> Bounds<F> { - self.global_analysis() - } -} - -impl<F: Float, T: GlobalAnalysis<F, Bounds<F>>> Bounded<F> for T {} - /// Shift of [`Support`] and [`Mapping`]; output of [`Support::shift`]. #[derive(Copy, Clone, Debug, Serialize)] // Serialize! but not implemented by Loc. pub struct Shift<T, F, const N: usize> {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/bounds.rs Sun Apr 27 15:56:43 2025 -0500 @@ -0,0 +1,246 @@ +/*! +Bounded and minimizable/maximizable mappings. +*/ + +use crate::instance::Instance; +use crate::loc::Loc; +use crate::mapping::RealMapping; +use crate::sets::{Cube, Set}; +use crate::types::{Float, Num}; + +/// Trait for globally analysing a property `A` of a [`Mapping`]. +/// +/// Typically `A` is an [`Aggregator`][super::aggregator::Aggregator] such as +/// [`Bounds`][super::aggregator::Bounds]. +pub trait GlobalAnalysis<F: Num, A> { + /// Perform global analysis of the property `A` of `Self`. + /// + /// As an example, in the case of `A` being [`Bounds`][super::aggregator::Bounds], + /// this function will return global upper and lower bounds for the mapping + /// represented by `self`. + fn global_analysis(&self) -> A; +} + +// default impl<F, A, N, L> GlobalAnalysis<F, A, N> for L +// where L : LocalAnalysis<F, A, N> { +// #[inline] +// fn global_analysis(&self) -> Bounds<F> { +// self.local_analysis(&self.support_hint()) +// } +// } + +/// Trait for locally analysing a property `A` of a [`Mapping`] (implementing [`Support`]) +/// within a [`Cube`]. +/// +/// Typically `A` is an [`Aggregator`][super::aggregator::Aggregator] such as +/// [`Bounds`][super::aggregator::Bounds]. +pub trait LocalAnalysis<F: Num, A, const N: usize>: GlobalAnalysis<F, A> { + /// Perform local analysis of the property `A` of `Self`. + /// + /// As an example, in the case of `A` being [`Bounds`][super::aggregator::Bounds], + /// this function will return upper and lower bounds within `cube` for the mapping + /// represented by `self`. + fn local_analysis(&self, cube: &Cube<F, N>) -> A; +} + +/// Trait for determining the upper and lower bounds of an float-valued [`Mapping`]. +/// +/// This is a blanket-implemented alias for [`GlobalAnalysis`]`<F, Bounds<F>>` +/// [`Mapping`] is not a supertrait to allow flexibility in the implementation of either +/// reference or non-reference arguments. +pub trait Bounded<F: Float>: GlobalAnalysis<F, Bounds<F>> { + /// Return lower and upper bounds for the values of of `self`. + #[inline] + fn bounds(&self) -> Bounds<F> { + self.global_analysis() + } +} + +impl<F: Float, T: GlobalAnalysis<F, Bounds<F>>> Bounded<F> for T {} + +/// A [`RealMapping`] that provides rough bounds as well as minimisation and maximisation. +pub trait MinMaxMapping<F: Float, const N: usize>: RealMapping<F, N> + Bounded<F> { + /// Maximise the mapping within stated value `tolerance`. + /// + /// At most `max_steps` refinement steps are taken. + /// Returns the approximate maximiser and the corresponding function value. + fn maximise(&mut self, tolerance: F, max_steps: usize) -> (Loc<F, N>, F); + + /// Maximise the mapping within stated value `tolerance` subject to a lower bound. + /// + /// At most `max_steps` refinement steps are taken. + /// Returns the approximate maximiser and the corresponding function value when one is found + /// above the `bound` threshold, otherwise `None`. + fn maximise_above( + &mut self, + bound: F, + tolerance: F, + max_steps: usize, + ) -> Option<(Loc<F, N>, F)>; + + /// Minimise the mapping within stated value `tolerance`. + /// + /// At most `max_steps` refinement steps are taken. + /// Returns the approximate minimiser and the corresponding function value. + fn minimise(&mut self, tolerance: F, max_steps: usize) -> (Loc<F, N>, F); + + /// Minimise the mapping within stated value `tolerance` subject to a lower bound. + /// + /// At most `max_steps` refinement steps are taken. + /// Returns the approximate minimiser and the corresponding function value when one is found + /// above the `bound` threshold, otherwise `None`. + fn minimise_below( + &mut self, + bound: F, + tolerance: F, + max_steps: usize, + ) -> Option<(Loc<F, N>, F)>; + + /// Verify that the mapping has a given upper `bound` within indicated `tolerance`. + /// + /// At most `max_steps` refinement steps are taken. + fn has_upper_bound(&mut self, bound: F, tolerance: F, max_steps: usize) -> bool; + + /// Verify that the mapping has a given lower `bound` within indicated `tolerance`. + /// + /// At most `max_steps` refinement steps are taken. + fn has_lower_bound(&mut self, bound: F, tolerance: F, max_steps: usize) -> bool; +} + +/// Upper and lower bounds on an `F`-valued function. +#[derive(Copy, Clone, Debug)] +pub struct Bounds<F>( + /// Lower bound + pub F, + /// Upper bound + pub F, +); + +impl<F: Copy> Bounds<F> { + /// Returns the lower bound + #[inline] + pub fn lower(&self) -> F { + self.0 + } + + /// Returns the upper bound + #[inline] + pub fn upper(&self) -> F { + self.1 + } +} + +impl<F: Float> Bounds<F> { + /// Returns a uniform bound. + /// + /// This is maximum over the absolute values of the upper and lower bound. + #[inline] + pub fn uniform(&self) -> F { + let &Bounds(lower, upper) = self; + lower.abs().max(upper.abs()) + } + + /// Construct a bounds, making sure `lower` bound is less than `upper` + #[inline] + pub fn corrected(lower: F, upper: F) -> Self { + if lower <= upper { + Bounds(lower, upper) + } else { + Bounds(upper, lower) + } + } + + /// Refine the lower bound + #[inline] + pub fn refine_lower(&self, lower: F) -> Self { + let &Bounds(l, u) = self; + debug_assert!(l <= u); + Bounds(l.max(lower), u.max(lower)) + } + + /// Refine the lower bound + #[inline] + pub fn refine_upper(&self, upper: F) -> Self { + let &Bounds(l, u) = self; + debug_assert!(l <= u); + Bounds(l.min(upper), u.min(upper)) + } +} + +impl<'a, F: Float> std::ops::Add<Self> for Bounds<F> { + type Output = Self; + #[inline] + fn add(self, Bounds(l2, u2): Self) -> Self::Output { + let Bounds(l1, u1) = self; + debug_assert!(l1 <= u1 && l2 <= u2); + Bounds(l1 + l2, u1 + u2) + } +} + +impl<'a, F: Float> std::ops::Mul<Self> for Bounds<F> { + type Output = Self; + #[inline] + fn mul(self, Bounds(l2, u2): Self) -> Self::Output { + let Bounds(l1, u1) = self; + debug_assert!(l1 <= u1 && l2 <= u2); + let a = l1 * l2; + let b = u1 * u2; + // The order may flip when negative numbers are involved, so need min/max + Bounds(a.min(b), a.max(b)) + } +} + +impl<F: Float> std::iter::Product for Bounds<F> { + #[inline] + fn product<I>(mut iter: I) -> Self + where + I: Iterator<Item = Self>, + { + match iter.next() { + None => Bounds(F::ZERO, F::ZERO), + Some(init) => iter.fold(init, |a, b| a * b), + } + } +} + +impl<F: Float> Set<F> for Bounds<F> { + fn contains<I: Instance<F>>(&self, item: I) -> bool { + let v = item.own(); + let &Bounds(l, u) = self; + debug_assert!(l <= u); + l <= v && v <= u + } +} + +impl<F: Float> Bounds<F> { + /// Calculate a common bound (glb, lub) for two bounds. + #[inline] + pub fn common(&self, &Bounds(l2, u2): &Self) -> Self { + let &Bounds(l1, u1) = self; + debug_assert!(l1 <= u1 && l2 <= u2); + Bounds(l1.min(l2), u1.max(u2)) + } + + /// Indicates whether `Self` is a superset of the argument bound. + #[inline] + pub fn superset(&self, &Bounds(l2, u2): &Self) -> bool { + let &Bounds(l1, u1) = self; + debug_assert!(l1 <= u1 && l2 <= u2); + l1 <= l2 && u2 <= u1 + } + + /// Returns the greatest bound contained by both argument bounds, if one exists. + #[inline] + pub fn glb(&self, &Bounds(l2, u2): &Self) -> Option<Self> { + let &Bounds(l1, u1) = self; + debug_assert!(l1 <= u1 && l2 <= u2); + let l = l1.max(l2); + let u = u1.min(u2); + debug_assert!(l <= u); + if l < u { + Some(Bounds(l, u)) + } else { + None + } + } +}
--- a/src/lib.rs Sun Apr 27 15:45:40 2025 -0500 +++ b/src/lib.rs Sun Apr 27 15:56:43 2025 -0500 @@ -1,48 +1,48 @@ // The main documentation is in the README. #![doc = include_str!("../README.md")] - // We use unicode. We would like to use much more of it than Rust allows. // Live with it. Embrace it. #![allow(uncommon_codepoints)] #![allow(mixed_script_confusables)] #![allow(confusable_idents)] - -#![cfg_attr(feature = "nightly", - feature(maybe_uninit_array_assume_init,maybe_uninit_slice), +#![cfg_attr( + feature = "nightly", + feature(maybe_uninit_array_assume_init, maybe_uninit_slice), feature(float_minimum_maximum), feature(get_mut_unchecked), - feature(cow_is_borrowed), + feature(cow_is_borrowed) )] -pub mod types; -pub mod instance; pub mod collection; -pub mod nanleast; pub mod error; +pub mod euclidean; +pub mod instance; +pub mod maputil; +pub mod nanleast; +pub mod norms; pub mod parallelism; -pub mod maputil; pub mod tuple; -pub mod euclidean; -pub mod norms; +pub mod types; #[macro_use] pub mod loc; +pub mod bisection_tree; +pub mod bounds; +pub mod coefficients; +pub mod convex; +pub mod direct_product; +pub mod discrete_gradient; +pub mod fe_model; pub mod iter; -pub mod linops; pub mod iterate; -pub mod tabledump; -pub mod logger; +pub mod lingrid; +pub mod linops; pub mod linsolve; -pub mod lingrid; -pub mod sets; +pub mod logger; pub mod mapping; -pub mod coefficients; -pub mod fe_model; -pub mod bisection_tree; +pub(crate) mod metaprogramming; pub mod nalgebra_support; -pub(crate) mod metaprogramming; -pub mod direct_product; -pub mod convex; -pub mod discrete_gradient; pub mod operator_arithmetic; +pub mod sets; +pub mod tabledump; pub use types::*;