Skip to content

Instantly share code, notes, and snippets.

@ExpHP
Created July 29, 2018 22:52
Show Gist options
  • Select an option

  • Save ExpHP/bed01185efadf28a5b44768a0736617b to your computer and use it in GitHub Desktop.

Select an option

Save ExpHP/bed01185efadf28a5b44768a0736617b to your computer and use it in GitHub Desktop.
diff --git a/crates.d/crates.toml b/crates.d/crates.toml
index d5c2351..480de2a 100644
--- a/crates.d/crates.toml
+++ b/crates.d/crates.toml
@@ -85,3 +85,4 @@ lammps-sys = { tag = "v0.3.0", git = "https://github.com/ExpHP/lammps-sys" }
rayon = "0.8"
vasp-poscar = "0.1.1"
path_abs = { rev = "72c767cfabe27", git = "https://github.com/vitiral/path_abs" }
+frunk = "0.1.36"
diff --git a/crates.d/rsp2-tasks.Cargo.toml b/crates.d/rsp2-tasks.Cargo.toml
index a7a0862..c44bc29 100644
--- a/crates.d/rsp2-tasks.Cargo.toml
+++ b/crates.d/rsp2-tasks.Cargo.toml
@@ -40,3 +40,4 @@ path = "lib.rs"
!!serde-json
!!serde-yaml
!!path-abs
+!!frunk
diff --git a/src/tasks/Cargo.toml b/src/tasks/Cargo.toml
index c1d320e..415ebfa 100644
--- a/src/tasks/Cargo.toml
+++ b/src/tasks/Cargo.toml
@@ -45,3 +45,4 @@ serde_derive = "1"
serde_json = "1"
serde_yaml = "0.7"
path_abs = { rev = "72c767cfabe27", git = "https://github.com/vitiral/path_abs" }
+frunk = "0.1.36"
diff --git a/src/tasks/cmd/ev_analyses.rs b/src/tasks/cmd/ev_analyses.rs
index 0fe101a..e556900 100644
--- a/src/tasks/cmd/ev_analyses.rs
+++ b/src/tasks/cmd/ev_analyses.rs
@@ -1,14 +1,16 @@
-
-
-use ::errors::{Result, ok};
use ::ui::color::{ColorByRange, PaintAs, NullPainter};
-use ::ui::cfg_merging::{no_summary, merge_summaries, make_nested_mapping};
+use ::ui::cfg_merging::{merge_summaries, make_nested_mapping, no_summary};
use ::util::zip_eq;
use ::types::basis::Basis3;
use ::math::bands::{GammaUnfolder, ScMatrix};
+use ::std::rc::Rc;
+use ::std::mem;
+use ::traits::alternate::{FnOnce, FnMut, Fn, StdFnMut, CallT};
+use ::std::cell::RefCell;
+use ::rsp2_tasks_config::Settings;
+use ::errors::{StdResult, Result, ok};
#[allow(unused)] // compiler bug
use ::itertools::Itertools;
-use ::rsp2_tasks_config::Settings;
#[allow(unused)] // compiler bug
use ::rsp2_structure::{CoordStructure, Part, Partition};
@@ -16,6 +18,718 @@ use ::rsp2_structure::{CoordStructure, Part, Partition};
use ::std::fmt;
use ::serde_yaml::Value as YamlValue;
+mod cached {
+ use super::*;
+ use ::traits::alternate::{FnOnce};
+ use self::inductive::hlist::{HList1, HNil};
+
+ // NOTE: All clones must be tied to the same cache.
+ pub trait Cached: Clone {
+ type Value;
+
+ fn call_cached(&self) -> Rc<Self::Value>;
+ }
+
+ pub struct Constant<A>(Rc<A>);
+ impl<A> Clone for Constant<A> {
+ fn clone(&self) -> Self { Constant(self.0.clone()) }
+ }
+
+ impl<A> Constant<A> {
+ pub fn new(a: A) -> Self { Constant(Rc::new(a)) }
+ }
+
+ impl<A> Cached for Constant<A> {
+ type Value = A;
+ fn call_cached(&self) -> Rc<A> { self.0.clone() }
+ }
+
+ pub use self::function::Function;
+ mod function {
+ use super::*;
+
+ pub struct Function<R, F>(Rc<RefCell<Inner<R, F>>>);
+ impl<R, F> Clone for Function<R, F> {
+ fn clone(&self) -> Self { Function(self.0.clone()) }
+ }
+
+ enum Inner<R, F> {
+ Result(Rc<R>),
+ Func(F),
+ MidComputation,
+ }
+
+ impl<R, F> Cached for Function<R, F> where F: FnOnce<HNil, Output=Rc<R>> {
+ type Value = R;
+
+ fn call_cached(&self) -> Rc<R> {
+ let mut inner = self.0.borrow_mut();
+ let result = match mem::replace(&mut *inner, Inner::MidComputation) {
+ Inner::MidComputation => panic!("detected cyclic dependency in Cached objects!"),
+ Inner::Func(f) => f.call_once(HNil),
+ Inner::Result(r) => r,
+ };
+ *inner = Inner::Result(result.clone());
+ result
+ }
+ }
+ }
+
+ pub use self::map::Map;
+ mod map {
+ use super::*;
+
+ pub struct Map<A, R, F>(Function<R, Closure<A, F>>);
+ impl<A, R, F> Clone for Map<A, R, F> {
+ fn clone(&self) -> Self { Map(self.0.clone()) }
+ }
+
+ struct Closure<A, F> {
+ arg: A,
+ continuation: F,
+ }
+
+ impl<W, A, R, F> FnOnce<HNil> for Closure<A, F>
+ where A: Cached<Value=W>, F: FnOnce<HList1<Rc<W>>, Output=Rc<R>>,
+ {
+ type Output = Rc<R>;
+
+ fn call_once(self, HNil: HNil) -> Rc<R>
+ { self.continuation.call_once(hlist![self.arg.call_cached()]) }
+ }
+
+ impl<W, A, R, F> Cached for Map<A, R, F>
+ where A: Cached<Value=W>, F: FnOnce<HList1<Rc<W>>, Output=Rc<R>>,
+ {
+ type Value = R;
+
+ fn call_cached(&self) -> Rc<R>
+ { self.0.call_cached() }
+ }
+ }
+
+ pub use self::and_then::AndThen;
+ mod and_then {
+ use super::*;
+
+ pub struct AndThen<A, R, F>(Function<R, Closure<A, F>>);
+ impl<A, R, F> Clone for AndThen<A, R, F> {
+ fn clone(&self) -> Self { AndThen(self.0.clone()) }
+ }
+
+ struct Closure<A, F> {
+ arg: A,
+ continuation: F,
+ }
+
+ impl<D, W, A, R, F> FnOnce<HNil> for Closure<A, F>
+ where A: Cached<Value=W>, F: FnOnce<HList1<Rc<W>>, Output=D>, D: Cached<Value=R>,
+ {
+ type Output = Rc<R>;
+
+ fn call_once(self, HNil: HNil) -> Rc<R>
+ { self.continuation.call_once(hlist![self.arg.call_cached()]).call_cached() }
+ }
+
+ impl<D, W, A, R, F> Cached for AndThen<A, R, F>
+ where A: Cached<Value=W>, F: FnOnce<HList1<Rc<W>>, Output=D>, D: Cached<Value=R>,
+ {
+ type Value = R;
+
+ fn call_cached(&self) -> Rc<R>
+ { self.0.call_cached() }
+ }
+ }
+}
+
+mod closures {
+ use super::*;
+ use self::inductive::hlist::{HList, HNil, HCons, HList1, HList2, h_cons};
+
+ #[derive(Debug, Copy, Clone, Default)]
+ pub struct Id;
+ derive_alternate_fn! {
+ impl[X] Fn<Hlist![X]> for Id {
+ type Output = X;
+
+ fn call(&self, hlist_pat![x]: Hlist![X]) -> X
+ { x }
+ }
+ }
+
+ /// Variadic function that puts all its arguments into one HList.
+ ///
+ /// This function has no inverse, since a function can only have one output.
+ /// (instead, you must use an HOF adapter like `OnHLists`)
+ #[derive(Debug, Copy, Clone, Default)]
+ pub struct MakeHList;
+ derive_alternate_fn! {
+ impl[X: HList] Fn<X> for MakeHList {
+ type Output = X;
+
+ fn call(&self, x: X) -> X
+ { x }
+ }
+ }
+
+ #[allow(unused)]
+ pub type SwapT<List> = <Swap as FnOnce<List>>::Output;
+
+ /// Swap the first two elements of an HList.
+ #[derive(Debug, Copy, Clone, Default)]
+ pub struct Swap;
+ derive_alternate_fn! {
+ impl[A, B, Rest: HList] Fn<HList1<HCons<A, HCons<B, Rest>>>> for Swap {
+ type Output = HCons<B, HCons<A, Rest>>;
+
+ fn call(&self, hlist_pat![list]: HList1<HCons<A, HCons<B, Rest>>>) -> Self::Output {
+ let (a, list) = list.pop();
+ let (b, list) = list.pop();
+ h_cons(b, h_cons(a, list))
+ }
+ }
+ }
+
+ /// Make a function of multiple arguments act on one HList.
+ ///
+ /// Similar in spirit to Haskell's `uncurry`.
+ #[derive(Debug, Copy, Clone, Default)]
+ pub struct OnHLists<F>(pub F);
+
+ /// Inverse of `OnHLists`. Make a function of HList act on multiple arguments.
+ pub type UnHLists<F> = Of<F, MakeHList>;
+ /// Fake constructor for `UnHLists`.
+ #[allow(bad_style)]
+ pub fn UnHLists<F>(f: F) -> UnHLists<F> { Of(f, MakeHList) }
+
+ impl<F, L: HList> Fn<HList1<L>> for OnHLists<F>
+ where F: Fn<L>,
+ {
+ fn call(&self, hlist_pat![list]: HList1<L>) -> Self::Output
+ { self.0.call(list) }
+ }
+
+ impl<F, L: HList> FnMut<HList1<L>> for OnHLists<F>
+ where F: FnMut<L>,
+ {
+ fn call_mut(&mut self, hlist_pat![list]: HList1<L>) -> Self::Output
+ { self.0.call_mut(list) }
+ }
+
+ impl<F, L: HList> FnOnce<HList1<L>> for OnHLists<F>
+ where F: FnOnce<L>,
+ {
+ type Output = F::Output;
+
+ fn call_once(self, hlist_pat![list]: HList1<L>) -> Self::Output
+ { self.0.call_once(list) }
+ }
+
+ /// Flip the first two arguments of a function.
+ ///
+ /// Even though this is just a type alias, you can construct one
+ /// by writing `Flip(f)`. (this is because in reality, there is
+ /// also a free function with the same name)
+ pub type Flip<F> = UnHLists<Of<OnHLists<F>, Swap>>;
+
+ /// Constructor for the type `Flip<F>`.
+ #[allow(bad_style)]
+ pub fn Flip<F>(f: F) -> Flip<F>
+ { UnHLists(Of(OnHLists(f), Swap)) }
+
+ 

#[allow(unused)]
+ fn test_on_hlist() {
+ assert_eq!(OnHLists(|a, b| a + b).call_once(hlist![hlist![1, 5]]), 6);
+ assert_eq!(UnHLists(|hlist_pat![a, b]| a + b).call_once(hlist![1, 5]), 6);
+
+ let v = vec![3];
+ assert_eq!(OnHLists(|| v).call_once(hlist![hlist![]]), vec![3]);
+ }
+
+ #[allow(unused)]
+ fn test_swap() {
+ assert_eq!(Swap.call_once(hlist![hlist![1, 5]]), hlist![5, 1]);
+ assert_eq!(Swap.call_once(hlist![hlist![1, 5, 4]]), hlist![5, 1, 4]);
+ }
+
+ #[allow(unused)]
+ fn test_flip() {
+ assert_eq!(Fn::call(&Flip(|a, b| a - b ), hlist![1, 5] ), 4);
+ assert_eq!(Fn::call(&Flip(|a, b, c| (a - b) * c), hlist![1, 5, 7]), 28);
+ }
+
+ #[allow(unused)]
+ pub type OfT<F, G, Args> = <Of<F, G> as FnOnce<Args>>::Output;
+ #[derive(Debug, Copy, Clone, Default)]
+ pub struct Of<F, G>(pub F, pub G);
+ impl<Args: HList, B, C, F, G> Fn<Args> for Of<F, G>
+ where
+ G: Fn<Args, Output=B>,
+ F: Fn<HList1<B>, Output=C>,
+ {
+ fn call(&self, args: Args) -> C
+ { self.0.call(hlist![self.1.call(args)]) }
+ }
+
+ impl<Args: HList, B, C, F, G> FnMut<Args> for Of<F, G>
+ where
+ G: FnMut<Args, Output=B>,
+ F: FnMut<HList1<B>, Output=C>,
+ {
+ fn call_mut(&mut self, args: Args) -> C
+ { self.0.call_mut(hlist![self.1.call_mut(args)]) }
+ }
+
+ impl<Args: HList, B, C, F, G> FnOnce<Args> for Of<F, G>
+ where
+ G: FnOnce<Args, Output=B>,
+ F: FnOnce<HList1<B>, Output=C>,
+ {
+ type Output = C;
+
+ fn call_once(self, args: Args) -> C
+ { self.0.call_once(hlist![self.1.call_once(args)]) }
+ }
+
+ #[allow(unused)]
+ fn test_of() {
+ struct A; struct B; struct C;
+ struct F; struct G;
+ impl FnOnce<HList1<A>> for F {
+ type Output = B;
+ fn call_once(self, _: HList1<A>) -> B { B }
+ }
+ impl FnOnce<HList1<B>> for G {
+ type Output = C;
+ fn call_once(self, _: HList1<B>) -> C { C }
+ }
+ let _: B = F.call_once(hlist![A]);
+ let _: C = G.call_once(hlist![B]);
+ let _: C = Of(G, F).call_once(hlist![A]);
+ }
+}
+
+/// Inductive types, to assist traits that simulate variadic generics.
+pub mod inductive {
+ use super::*;
+
+ pub use self::peano::{P0, Succ, IsPeano, Pred};
+ pub mod peano {
+ use super::*;
+
+ pub use self::constants::*;
+ pub mod constants {
+ use super::*;
+ pub use super::P0;
+ #[doc = "Peano integer."] pub type P1 = Succ<P0>;
+ #[doc = "Peano integer."] pub type P2 = Succ<P1>;
+ #[doc = "Peano integer."] pub type P3 = Succ<P2>;
+ #[doc = "Peano integer."] pub type P4 = Succ<P3>;
+ #[doc = "Peano integer."] pub type P5 = Succ<P4>;
+ #[doc = "Peano integer."] pub type P6 = Succ<P5>;
+ #[doc = "Peano integer."] pub type P7 = Succ<P6>;
+ #[doc = "Peano integer."] pub type P8 = Succ<P7>;
+ #[doc = "Peano integer."] pub type P9 = Succ<P8>;
+ }
+
+ /// The Peano integer zero. (base case)
+ #[derive(Debug, Default, Copy, Clone)]
+ pub struct P0;
+
+ /// A Peano integer incremented by 1. (inductive case)
+ #[derive(Debug, Default, Copy, Clone)]
+ pub struct Succ<A: IsPeano>(pub A);
+
+ /// Marker trait for Peano integers, for "type safety" within the type system.
+ pub trait IsPeano {}
+ impl IsPeano for P0 {}
+ impl<N: IsPeano> IsPeano for Succ<N> {}
+
+ /// Subtract 1 from a nonzero Peano integer.
+ ///
+ /// More accurately, this solves the equation `p + 1 = x` for `p`.
+ pub type Pred<A> = <A as Positive>::Pred;
+
+ pub trait Positive: IsPeano {
+ type Pred: IsPeano;
+ }
+
+ impl<N: IsPeano> Positive for Succ<N> {
+ type Pred = N;
+ }
+ }
+
+ pub mod hlist {
+ use super::*;
+
+ pub use frunk::hlist::{HList, HCons, HNil, h_cons};
+
+ // wake me up when clion can parse type macros properly
+ pub type HList1<A> = HCons<A, HNil>;
+ pub type HList2<A, B> = HCons<A, HList1<B>>;
+ pub type HList3<A, B, C> = HCons<A, HList2<B, C>>;
+ pub type HList4<A, B, C, D> = HCons<A, HList3<B, C, D>>;
+
+ //----------------
+
+ #[doc(hidden)]
+ pub type MapT<List, F> = <List as Map<F>>::Output;
+
+ pub trait Map<F>: HList {
+ type Output: HList;
+
+ fn map(self, f: F) -> Self::Output;
+ }
+
+ impl<F> Map<F> for HNil {
+ type Output = HNil;
+
+ #[inline(always)]
+ fn map(self, _: F) -> Self::Output
+ { HNil }
+ }
+
+ impl<A, B, Rest: Map<F>, F> Map<F> for HCons<A, Rest>
+ where F: FnMut<HList1<A>, Output=B>,
+ {
+ type Output = HCons<B, MapT<Rest, F>>;
+
+ #[inline(always)]
+ fn map(self, mut f: F) -> Self::Output {
+ let (a, rest) = self.pop();
+ h_cons(f.call_mut(hlist![a]), rest.map(f))
+ }
+ }
+
+ //----------------
+
+ pub struct HConsClosure;
+ derive_alternate_fn! {
+ impl[A, List: HList] Fn<Hlist![A, List]> for HConsClosure {
+ type Output = HCons<A, List>;
+
+ fn call(&self, hlist_pat![a, list]: Hlist![A, List]) -> Self::Output
+ { h_cons(a, list) }
+ }
+ }
+
+
+ pub struct ReverseClosure;
+ derive_alternate_fn! {
+ impl[List: ::frunk::hlist::IntoReverse] Fn<Hlist![List]> for hlist::ReverseClosure {
+ type Output = List::Output;
+
+ fn call(&self, hlist_pat![list]: Hlist![List]) -> Self::Output
+ { list.into_reverse() }
+ }
+ }
+
+ #[allow(unused)]
+ fn test_reverse() {
+ struct A; struct B; struct C;
+ let hlist_pat![] = ReverseClosure.call_once(hlist![hlist![]]);
+ let hlist_pat![A] = ReverseClosure.call_once(hlist![hlist![A]]);
+ let hlist_pat![B, A] = ReverseClosure.call_once(hlist![hlist![A, B]]);
+ let hlist_pat![C, B, A] = ReverseClosure.call_once(hlist![hlist![A, B, C]]);
+ }
+ }
+}
+
+pub mod option {
+ use super::*;
+ use ::std::ops::{Deref, DerefMut};
+ use ::std::marker::PhantomData;
+ use self::inductive::hlist::{self, HNil, HCons, HList, HList1, HList2};
+
+ #[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Default)]
+ pub struct Just<T>(pub T);
+ #[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
+ pub struct Nothing<T>(pub PhantomData<T>);
+ pub type Maybe<T> = Option<T>;
+
+ // for type-level type-checking
+ pub trait IsMaybe { }
+ impl<T> IsMaybe for Just<T> { }
+ impl<T> IsMaybe for Maybe<T> { }
+ impl<T> IsMaybe for Nothing<T> { }
+
+ impl<T> Deref for Just<T> {
+ type Target = T;
+
+ fn deref(&self) -> &Self::Target
+ { &self.0 }
+ }
+
+ impl<T> DerefMut for Just<T> {
+ fn deref_mut(&mut self) -> &mut Self::Target
+ { &mut self.0 }
+ }
+
+ impl<T> Default for Nothing<T> { fn default() -> Self { Nothing(Default::default()) } }
+ impl<T> Clone for Nothing<T> { fn clone(&self) -> Self { Default::default() } }
+ impl<T> Copy for Nothing<T> { }
+
+ #[allow(unused)]
+ pub type MapT<A, F> = <A as Map<F>>::Output;
+ pub trait Map<F>: Sized + IsMaybe {
+ type Output;
+
+ fn map(self, f: F) -> Self::Output;
+ }
+
+ impl<A, B, F: FnOnce<HList1<A>, Output=B>> Map<F> for Just<A> {
+ type Output = Just<B>;
+
+ fn map(self, f: F) -> Self::Output
+ { Just(f.call_once(hlist![self.0])) }
+ }
+
+ impl<A, B, F: FnOnce<HList1<A>, Output=B>> Map<F> for Maybe<A> {
+ type Output = Maybe<B>;
+
+ fn map(self, f: F) -> Self::Output
+ { Option::map(self, |x| f.call_once(hlist![x])) }
+ }
+
+ impl<A, B, F: FnOnce<HList1<A>, Output=B>> Map<F> for Nothing<A> {
+ type Output = Nothing<B>;
+
+ fn map(self, _: F) -> Self::Output
+ { Default::default() }
+ }
+
+// #[allow(unused)]
+// pub type ZipT<A, B> = ZipWithT<closures::MakeTuple, A, B>;
+// pub trait Zip<Rhs: IntoMaybe>: ZipWith<Rhs, closures::MakeTuple> {
+// fn zip(self, other: Rhs) -> Self::Output;
+// }
+// impl<A, B: IntoMaybe> Zip<B> for A where A: ZipWith<B, closures::MakeTuple> {
+// fn zip(self, other: B) -> Self::Output
+// { self.zip_with(other, closures::MakeTuple) }
+// }
+
+ #[allow(unused)]
+ pub type ZipWithT<F, A, B> = <A as ZipWith<B, F>>::Output;
+ pub trait ZipWith<Rhs: IntoMaybe, F>: Sized + IntoMaybe {
+ type Output;
+
+ fn zip_with(self, other: Rhs, f: F) -> Self::Output;
+ }
+
+ pub use self::list_zip::{ListZip, ListZipT};
+ mod list_zip {
+ use super::*;
+
+ #[allow(unused)]
+ pub type ListZipT<List> = <List as ListZip>::Output;
+ pub trait ListZip: HList {
+ type Output: IsMaybe;
+
+ fn list_zip(self) -> Self::Output;
+ }
+
+ impl<List: Impl> ListZip for List
+ where
+ ImplT<Self>: Map<hlist::ReverseClosure>,
+ MapT<ImplT<Self>, hlist::ReverseClosure>: IsMaybe,
+ {
+ type Output = MapT<ImplT<Self>, hlist::ReverseClosure>;
+
+ fn list_zip(self) -> Self::Output
+ {
+ let maybe_reversed = self.rec(Just(HNil));
+ maybe_reversed.map(hlist::ReverseClosure)
+ }
+ }
+
+ pub type ImplInit = Just<HNil>;
+ pub type ImplT<List> = <List as Impl>::Output;
+ pub trait Impl<Acc: IsMaybe = ImplInit>: HList {
+ type Output: IsMaybe;
+
+ fn rec(self, acc: Acc) -> Self::Output;
+ }
+
+ impl<Acc: IsMaybe> Impl<Acc> for HNil {
+ type Output = Acc;
+
+ fn rec(self, acc: Acc) -> Self::Output { acc }
+ }
+
+ impl<
+ A: IntoMaybe<TyArg=AInner> + ZipWith<Acc, hlist::HConsClosure, Output=Z>,
+ Acc: IntoMaybe<TyArg=AccInner>,
+ AccInner,
+ AInner,
+ Z: IsMaybe,
+ Rest: Impl<Z, Output=R>,
+ R: IsMaybe,
+ > Impl<Acc> for HCons<A, Rest>
+ {
+ type Output = R;
+
+ fn rec(self, acc: Acc) -> Self::Output
+ { self.tail.rec(self.head.zip_with(acc, hlist::HConsClosure)) }
+ }
+ }
+
+ #[allow(unused)]
+ pub type AsRefT<A> = <A as AsRef>::Output;
+ pub trait AsRef {
+ type Output;
+
+ fn as_ref(self) -> Self::Output;
+ }
+
+ impl<'a, A> AsRef for &'a Maybe<A> {
+ type Output = Maybe<&'a A>;
+
+ fn as_ref(self) -> Self::Output
+ { Option::as_ref(self) }
+ }
+
+ impl<'a, A> AsRef for &'a Just<A> {
+ type Output = Just<&'a A>;
+
+ fn as_ref(self) -> Self::Output
+ { Just(&self.0) }
+ }
+
+ impl<'a, A> AsRef for &'a Nothing<A> {
+ type Output = Nothing<&'a A>;
+
+ fn as_ref(self) -> Self::Output
+ { Default::default() }
+ }
+
+ #[derive(Debug, Copy, Clone, Default)]
+ pub struct AsRefClosure;
+ derive_alternate_fn! {
+ impl[A: AsRef] Fn<HList1<A>> for AsRefClosure {
+ type Output = AsRefT<A>;
+
+ fn call(&self, hlist_pat![a]: HList1<A>) -> Self::Output
+ { a.as_ref() }
+ }
+ }
+
+ pub trait IntoMaybe: IsMaybe {
+ type TyArg;
+
+ fn into_maybe(self) -> Maybe<Self::TyArg>;
+ fn as_maybe(&self) -> Maybe<&Self::TyArg>;
+ }
+
+ impl<A> IntoMaybe for Just<A> {
+ type TyArg = A;
+
+ fn into_maybe(self) -> Maybe<A> { Some(self.0) }
+ fn as_maybe(&self) -> Maybe<&A> { Some(&self.0) }
+ }
+
+ impl<A> IntoMaybe for Maybe<A> {
+ type TyArg = A;
+
+ fn into_maybe(self) -> Maybe<A> { self }
+ fn as_maybe(&self) -> Maybe<&A> { self.as_ref() }
+ }
+
+ impl<A> IntoMaybe for Nothing<A> {
+ type TyArg = A;
+
+ fn into_maybe(self) -> Maybe<A> { None }
+ fn as_maybe(&self) -> Maybe<&A> { None }
+ }
+
+ pub trait ExpectFromOption<B>: IsMaybe {
+ fn expect_from(b: Option<B>) -> Self;
+ }
+
+ impl<A> ExpectFromOption<A> for Maybe<A> {
+ fn expect_from(a: Option<A>) -> Self { a }
+ }
+
+ impl<A> ExpectFromOption<A> for Just<A> {
+ fn expect_from(a: Option<A>) -> Self { Just(a.unwrap()) }
+ }
+
+ impl<A> ExpectFromOption<A> for Nothing<A> {
+ fn expect_from(a: Option<A>) -> Self
+ { match a {
+ Some(_) => panic!("expect_from: expected nothing, got Some(_)!"),
+ None => Default::default(),
+ }}
+ }
+
+ #[allow(unused)]
+ pub type FoldOkT<A> = <A as FoldOk>::Output;
+ pub trait FoldOk {
+ type Output;
+
+ fn fold_ok(self) -> Self::Output;
+ }
+
+ impl<A, E> FoldOk for Maybe<StdResult<A, E>> {
+ type Output = StdResult<Maybe<A>, E>;
+
+ fn fold_ok(self) -> Self::Output
+ { match self {
+ Some(x) => Ok(Some(x?)),
+ None => Ok(None),
+ }}
+ }
+
+ impl<A, E> FoldOk for Just<StdResult<A, E>> {
+ type Output = StdResult<Just<A>, E>;
+
+ fn fold_ok(self) -> Self::Output
+ { Ok(Just(self.0?)) }
+ }
+
+
+ impl<A, E> FoldOk for Nothing<StdResult<A, E>> {
+ type Output = StdResult<Nothing<A>, E>;
+
+ fn fold_ok(self) -> Self::Output
+ { Ok(Default::default()) }
+ }
+
+ macro_rules! impl_zip_with {
+ ( $( [$A:ident, $B:ident] => $C:ident;)* )
+ => {
+ $(
+ impl<A, B, C, F: FnOnce<HList2<A, B>, Output=C>> ZipWith<$B<B>, F> for $A<A>
+ where F: FnOnce<HList2<A, B>>,
+ {
+ type Output = $C<C>;
+
+ fn zip_with(self, other: $B<B>, f: F) -> Self::Output {
+ ExpectFromOption::expect_from({
+ match (self.into_maybe(), other.into_maybe()) {
+ (Some(a), Some(b)) => Some(f.call_once(hlist![a, b])),
+ _ => None,
+ }
+ })
+ }
+ }
+ )*
+ };
+ }
+
+ impl_zip_with!{
+ [Nothing, Nothing] => Nothing;
+ [ Maybe, Nothing] => Nothing;
+ [ Just, Nothing] => Nothing;
+ [Nothing, Maybe] => Nothing;
+ [Nothing, Just] => Nothing;
+ [ Maybe, Maybe] => Maybe;
+ [ Maybe, Just] => Maybe;
+ [ Just, Maybe] => Maybe;
+ [ Just, Just] => Just;
+ }
+}
+
+
#[derive(Debug, Clone)] pub struct AtomCoordinates(pub CoordStructure);
#[derive(Debug, Clone)] pub struct AtomLayers(pub Vec<usize>);
#[derive(Debug, Clone)] pub struct LayerScMatrices(pub Vec<ScMatrix>);
@@ -25,49 +739,105 @@ use ::serde_yaml::Value as YamlValue;
pub use self::gamma_system_analysis::GammaSystemAnalysis;
pub mod gamma_system_analysis {
use super::*;
+ use self::inductive::hlist::{self, HList};
+
+ pub trait Analyze<F> {
+ type Output;
+
+ fn analyze(self, f: F) -> Self::Output;
+ }
+
+ impl<'a, F, Z, M, As: HList, Rs: HList, Out> Analyze<As> for F
+ where
+ As: hlist::Map<option::AsRefClosure, Output=Rs>,
+ Rs: option::ListZip<Output=Z>,
+ Z: option::Map<closures::OnHLists<F>, Output=M>,
+ M: option::FoldOk<Output=Out>,
+ {
+ type Output = Out;
+
+ fn analyze(self, args: As) -> Self::Output {
+ args.map(option::AsRefClosure)
+ .list_zip()
+ .map(closures::OnHLists(self))
+ .fold_ok()
+ }
+ }
- pub struct Input<'a> {
- pub atom_coords: &'a Option<AtomCoordinates>,
- pub atom_layers: &'a Option<AtomLayers>,
- pub layer_sc_mats: &'a Option<LayerScMatrices>,
- pub ev_frequencies: &'a Option<EvFrequencies>,
- pub ev_eigenvectors: &'a Option<EvEigenvectors>,
+ pub struct Input<'a, A, B, C, D, E>
+ where A: 'a, B: 'a, C: 'a, D: 'a, E: 'a,
+ {
+ pub atom_coords: &'a A,
+ pub atom_layers: &'a B,
+ pub layer_sc_mats: &'a C,
+ pub ev_frequencies: &'a D,
+ pub ev_eigenvectors: &'a E,
}
- pub struct GammaSystemAnalysis {
- pub ev_frequencies: Option<EvFrequencies>,
- pub ev_acousticness: Option<EvAcousticness>,
- pub ev_polarization: Option<EvPolarization>,
- pub ev_layer_gamma_probs: Option<EvLayerGammaProbs>,
- pub ev_layer_acousticness: Option<EvLayerAcousticness>,
+ pub struct GammaSystemAnalysis<A, B, C, D, E>
+ where
+ {
+ pub ev_frequencies: A,
+ pub ev_acousticness: B,
+ pub ev_polarization: C,
+ pub ev_layer_gamma_probs: D,
+ pub ev_layer_acousticness: E,
}
- impl<'a> Input<'a> {
- pub fn compute(&self) -> Result<GammaSystemAnalysis>
+ impl<
+ 'a,
+ InAtomCoordinates,
+ InAtomLayers,
+ InLayerScMatrices,
+ InEvFrequencies,
+ InEvEigenvectors,
+ > Input<
+ 'a,
+ InAtomCoordinates,
+ InAtomLayers,
+ InLayerScMatrices,
+ InEvFrequencies,
+ InEvEigenvectors,
+ > {
+ pub fn compute<
+ OutEvAcousticness,
+ OutEvPolarization,
+ OutEvLayerGammaProbs,
+ OutEvLayerAcousticness,
+ >(&self) -> Result<GammaSystemAnalysis<
+ InEvFrequencies,
+ OutEvAcousticness,
+ OutEvPolarization,
+ OutEvLayerGammaProbs,
+ OutEvLayerAcousticness,
+ >>
+ where
+ InEvFrequencies: Clone,
+ ev_acousticness::Analysis: Analyze<hlist::HList1<&'a InEvEigenvectors>, Output=Result<OutEvAcousticness>>,
+ ev_polarization::Analysis: Analyze<hlist::HList1<&'a InEvEigenvectors>, Output=Result<OutEvPolarization>>,
+ ev_layer_gamma_probs::Analysis: Analyze<hlist::HList4<&'a InAtomLayers, &'a InAtomCoordinates, &'a InLayerScMatrices, &'a InEvEigenvectors>, Output=Result<OutEvLayerGammaProbs>>,
+ ev_layer_acousticness::Analysis: Analyze<hlist::HList2<&'a InAtomLayers, &'a InEvEigenvectors>, Output=Result<OutEvLayerAcousticness>>,
{ok({
let Input {
atom_coords, atom_layers, layer_sc_mats,
ev_frequencies, ev_eigenvectors,
} = *self;
- // This is a bit repetitive, perhaps, but the upshot is that
- // it is *impossible* to make a mistake here; it would not compile.
-
- let ev_acousticness = ev_acousticness::Input {
+ let ev_acousticness = ev_acousticness::Analysis.analyze(hlist![
ev_eigenvectors,
- }.maybe_compute()?;
+ ])?;
- let ev_polarization = ev_polarization::Input {
+ let ev_polarization = ev_polarization::Analysis.analyze(hlist![
ev_eigenvectors,
- }.maybe_compute()?;
+ ])?;
- let ev_layer_acousticness = ev_layer_acousticness::Input {
- ev_eigenvectors, atom_layers,
- }.maybe_compute()?;
+ let ev_layer_gamma_probs = ev_layer_gamma_probs::Analysis.analyze(hlist![
+ atom_layers, atom_coords, layer_sc_mats, ev_eigenvectors,
+ ])?;
- let ev_layer_gamma_probs = ev_layer_gamma_probs::Input {
- ev_eigenvectors, atom_layers, atom_coords, layer_sc_mats,
- }.maybe_compute()?;
+ let ev_layer_acousticness = ev_layer_acousticness::Analysis.analyze(hlist![
+ atom_layers, ev_eigenvectors,
+ ])?;
let ev_frequencies = ev_frequencies.clone();
@@ -104,24 +874,13 @@ macro_rules! wrap_maybe_compute {
$( { $($Thing_body_if_brace)* } )*
$($( ( $($Thing_body_if_paren)* ) )* ; )*
- pub struct Input<'a> {
- $(pub $arg : &'a Option<$Arg> ,)*
- }
+ pub struct Analysis;
+ impl<'a> FnOnce<Hlist![$(&'a $Arg,)*]> for Analysis {
+ type Output = Result<$Thing>;
- impl<'a> Input<'a> {
- pub fn maybe_compute(self) -> Result<Option<$Thing>> {
- $(
- let $arg = match self.$arg {
- &Some(ref x) => x,
- &None => return Ok(None),
- };
- )*
- Ok(Some(compute($($arg),*)?))
- }
+ fn call_once(self, hlist_pat![$($arg),*] : Hlist![$(&'a $Arg),*]) -> Self::Output
+ $fn_body
}
-
- fn compute($($arg: &$Arg),*) -> Result<$Thing>
- $fn_body
}
)*
}
@@ -132,22 +891,17 @@ pub use self::ev_acousticness::EvAcousticness;
pub mod ev_acousticness {
use super::*;
+ use self::inductive::hlist::HList1;
+
pub struct EvAcousticness(pub Vec<f64>);
- pub struct Input<'a> {
- pub ev_eigenvectors: &'a Option<EvEigenvectors>,
- }
- impl<'a> Input<'a> {
- pub fn maybe_compute(self) -> Result<Option<EvAcousticness>> {
- let ev_eigenvectors = match self.ev_eigenvectors {
- &Some(ref x) => x,
- &None => return Ok(None),
- };
- Ok(Some(compute(ev_eigenvectors)?))
+ pub struct Analysis;
+ impl<'a> FnOnce<HList1<&'a EvEigenvectors>> for Analysis {
+ type Output = Result<EvAcousticness>;
+
+ fn call_once(self, hlist_pat![ev_eigenvectors]: HList1<&EvEigenvectors>) -> Self::Output {
+ Ok(EvAcousticness((ev_eigenvectors.0).0.iter().map(|ket| ket.acousticness()).collect()))
}
}
- fn compute(ev_eigenvectors: &EvEigenvectors) -> Result<EvAcousticness> {
- Ok(EvAcousticness((ev_eigenvectors.0).0.iter().map(|ket| ket.acousticness()).collect()))
- }
}
wrap_maybe_compute! {
@@ -254,8 +1008,28 @@ pub enum ColumnsMode {
ForMachines,
}
-impl GammaSystemAnalysis {
+impl<A, B, C, D, E> GammaSystemAnalysis<A, B, C, D, E>
+where
+ A: option::IntoMaybe<TyArg=EvFrequencies>,
+ B: option::IntoMaybe<TyArg=EvAcousticness>,
+ C: option::IntoMaybe<TyArg=EvPolarization>,
+ D: option::IntoMaybe<TyArg=EvLayerGammaProbs>,
+ E: option::IntoMaybe<TyArg=EvLayerAcousticness>,
+{
pub fn make_columns(&self, mode: ColumnsMode) -> Option<Columns> {
+ let &GammaSystemAnalysis {
+ ref ev_frequencies,
+ ref ev_acousticness,
+ ref ev_polarization,
+ ref ev_layer_gamma_probs,
+ ref ev_layer_acousticness,
+ } = self;
+ let ev_frequencies = ev_frequencies.as_maybe();
+ let ev_acousticness = ev_acousticness.as_maybe();
+ let ev_polarization = ev_polarization.as_maybe();
+ let ev_layer_gamma_probs = ev_layer_gamma_probs.as_maybe();
+ let ev_layer_acousticness = ev_layer_acousticness.as_maybe();
+
use self::Color::{Colorful, Colorless};
let mut columns = vec![];
@@ -264,7 +1038,7 @@ impl GammaSystemAnalysis {
let fix2 = |c, title: &str, data: &_| fixed_prob_column(c, Precision(2), title, data);
let dp = display_prob_column;
- if let Some(ref data) = self.ev_frequencies {
+ if let Some(ref data) = ev_frequencies {
let col = Columns {
header: "Frequency(cm-1)".into(),
entries: data.0.to_vec(),
@@ -276,21 +1050,21 @@ impl GammaSystemAnalysis {
))
};
- if let Some(ref data) = self.ev_acousticness {
+ if let Some(ref data) = ev_acousticness {
columns.push(match mode {
ColumnsMode::ForHumans => dp(Colorful, "Acoust.", &data.0),
ColumnsMode::ForMachines => fix2(Colorless, "Acou", &data.0),
})
};
- if let Some(ref data) = self.ev_layer_acousticness {
+ if let Some(ref data) = ev_layer_acousticness {
columns.push(match mode {
ColumnsMode::ForHumans => dp(Colorful, "Layer", &data.0),
ColumnsMode::ForMachines => fix2(Colorless, "Lay.", &data.0),
})
};
- if let Some(ref data) = self.ev_polarization {
+ if let Some(ref data) = ev_polarization {
let data = |k| data.0.iter().map(|v| v[k]).collect_vec();
let name = |k| "XYZ".chars().nth(k).unwrap().to_string();
@@ -316,7 +1090,7 @@ impl GammaSystemAnalysis {
});
}
- if let Some(ref data) = self.ev_layer_gamma_probs {
+ if let Some(ref data) = ev_layer_gamma_probs {
for (n, probs) in data.0.iter().enumerate() {
columns.push(match mode {
ColumnsMode::ForHumans => fix1(Colorful, &format!("G{:02}", n+1), &probs),
@@ -337,9 +1111,7 @@ impl GammaSystemAnalysis {
}),
}
}
-}
-impl GammaSystemAnalysis {
pub fn make_summary(&self, settings: &Settings) -> YamlValue {
let GammaSystemAnalysis {
ref ev_acousticness, ref ev_polarization,
@@ -350,14 +1122,14 @@ impl GammaSystemAnalysis {
// This is where the newtypes start to get in the way;
// turn as many things as we can into Option<Vec<T>> (indexed by ket)
// for ease of composition.
- let frequency = ev_frequencies.as_ref().map(|d| d.0.to_vec());
- let acousticness = ev_acousticness.as_ref().map(|d| d.0.to_vec());
- let polarization = ev_polarization.as_ref().map(|d| d.0.to_vec());
- let layer_acousticness = ev_layer_acousticness.as_ref().map(|d| d.0.to_vec());
+ let frequency = ev_frequencies.as_maybe().map(|d| d.0.to_vec());
+ let acousticness = ev_acousticness.as_maybe().map(|d| d.0.to_vec());
+ let polarization = ev_polarization.as_maybe().map(|d| d.0.to_vec());
+ let layer_acousticness = ev_layer_acousticness.as_maybe().map(|d| d.0.to_vec());
// Work with Option<Vec<A>> as an applicative functor (for fixed length Vec)
fn map1<A, R, F>(a: &Option<Vec<A>>, mut f: F) -> Option<Vec<R>>
- where F: FnMut(&A) -> R
+ where F: for<'a> StdFnMut(&'a A) -> R
{
let a = a.as_ref()?;
Some(a.iter().map(|a| f(a)).collect())
@@ -365,7 +1137,7 @@ impl GammaSystemAnalysis {
// (haskell's LiftA2)
fn map2<B, A, R, F>(a: &Option<Vec<A>>, b: &Option<Vec<B>>, mut f: F) -> Option<Vec<R>>
- where F: FnMut(&A, &B) -> R
+ where F: for<'a, 'b> StdFnMut(&'a A, &'b B) -> R
{
Some({
zip_eq(a.as_ref()?, b.as_ref()?)
@@ -430,7 +1202,7 @@ impl GammaSystemAnalysis {
// For gamma probs, don't bother with all layers; just a couple.
[0, 1].iter().for_each(|&layer_n| {
- let probs = ev_layer_gamma_probs.as_ref().map(|d| d.0[layer_n].to_vec());
+ let probs = ev_layer_gamma_probs.as_maybe().map(|d| d.0[layer_n].to_vec());
let layer_key = format!("layer-{}", layer_n + 1);
let pred = at_least(settings.layer_gamma_threshold, &probs);
@@ -445,7 +1217,8 @@ impl GammaSystemAnalysis {
let items = tuples.into_iter().map(|(index, &(frequency, probability))| {
Item { index, frequency, probability }
}).collect_vec();
- let value = ::serde_yaml::to_value(&items).unwrap();
+
+ let value = ::serde_yaml::to_value(items).unwrap();
out.push(make_nested_mapping(&["layer-gammas", &layer_key], value));
}
});
@@ -524,6 +1297,7 @@ use self::columns::{Columns, Color, Precision, fixed_prob_column, display_prob_c
mod columns {
use super::*;
use std::iter::{Chain, Once, once};
+ use self::inductive::hlist::HList1;
#[derive(Debug, Clone)]
pub struct Columns<T = String> {
@@ -562,13 +1336,17 @@ mod columns {
/// Values are string-formatted by some mapping function, and optionally colorized
/// according to the magnitude of their value.
fn quick_column<C, D, F>(painter: &PaintAs<D, C>, header: &str, values: &[C], width: usize, mut show: F) -> Columns
- where
- C: PartialOrd,
- F: FnMut(&C) -> D,
- D: fmt::Display,
+ where
+ C: PartialOrd,
+ F: StdFnMut(&C) -> D,
+ D: fmt::Display,
{ Columns {
header: format!(" {:^width$}", header, width = width),
- entries: values.iter().map(|x| format!(" {}", painter.paint_as(x, show(x)))).collect(),
+ entries: {
+ values.iter()
+ .map(|x| format!(" {}", painter.paint_as(x, show(x))))
+ .collect()
+ },
}}
pub struct Precision(pub usize);
@@ -583,7 +1361,7 @@ mod columns {
Color::Colorful => Box::new(default_prob_color_range()),
Color::Colorless => Box::new(NullPainter),
};
- quick_column(&*painter, header, values, precision.0 + 2, |&x| FixedProb(x, precision.0))
+ quick_column(&*painter, header, values, precision.0 + 2, |&x: &_| FixedProb(x, precision.0))
}
pub fn display_prob_column(color: Color, header: &str, values: &[f64]) -> Columns
@@ -592,19 +1370,6 @@ mod columns {
Color::Colorful => Box::new(default_prob_color_range()),
Color::Colorless => Box::new(NullPainter),
};
- quick_column(&*painter, header, values, 7, |&x| DisplayProb(x))
+ quick_column(&*painter, header, values, 7, |&x: &_| DisplayProb(x))
}
}
-
-// let energy_per_atom = {
-// let f = |structure: ElementStructure| ok({
-// let na = structure.num_atoms() as f64;
-// lmp.build(structure)?.compute_value()? / na
-// });
-// let f_path = |s: &AsPath| ok(f(poscar::load(self.open(s)?)?)?);
-
-// let initial = f_path(&"initial.vasp")?;
-// let final_ = f_path(&"final.vasp")?;
-// let before_ev_chasing = f_path(&"structure-01.1.vasp")?;
-// EnergyPerAtom { initial, final_, before_ev_chasing }
-// };
diff --git a/src/tasks/cmd/mod.rs b/src/tasks/cmd/mod.rs
index 05dd721..e7a2255 100644
--- a/src/tasks/cmd/mod.rs
+++ b/src/tasks/cmd/mod.rs
@@ -139,6 +139,7 @@ impl TrialDir {
trace!("Computing eigensystem info");
let ev_analysis = {
use self::ev_analyses::*;
+ use self::ev_analyses::option::Just;
// (NOTE: all these Options look pointless now, but the layer
// stuff shall soon become optional.
@@ -146,23 +147,27 @@ impl TrialDir {
// The other Options *are* kind of pointless, but they simplifies the
// design of the analysis module)
gamma_system_analysis::Input {
- atom_coords: &Some(AtomCoordinates(structure.map_metadata_to(|_| ()))),
+ atom_coords: &Just(AtomCoordinates(structure.map_metadata_to(|_| ()))),
atom_layers: &Some(AtomLayers(atom_layers.clone())),
layer_sc_mats: &Some(LayerScMatrices(layer_sc_mats.clone())),
- ev_frequencies: &Some(EvFrequencies(evals.clone())),
- ev_eigenvectors: &Some(EvEigenvectors(evecs.clone())),
+ ev_frequencies: &Just(EvFrequencies(evals.clone())),
+ ev_eigenvectors: &Just(EvEigenvectors(evecs.clone())),
}.compute()?
};
{
- let file = self.create_file(format!("eigenvalues.{:02}", iteration))?;
- write_eigen_info_for_machines(&ev_analysis, file)?;
- write_eigen_info_for_humans(&ev_analysis, &mut |s| ok(info!("{}", s)))?;
+ let mut file = self.create_file(format!("eigenvalues.{:02}", iteration))?;
+ ev_analysis.make_columns(ev_analyses::ColumnsMode::ForMachines)
+ .expect("(bug) no columns, not even frequency?")
+ .into_iter().map(|s| ok(writeln!(file, "{}", s)?)).collect::<Result<()>>()?;
+ ev_analysis.make_columns(ev_analyses::ColumnsMode::ForHumans)
+ .expect("(bug) no columns, not even frequency?")
+ .into_iter().map(|s| ok(info!("{}", s))).collect::<Result<()>>()?;
}
let mut structure = structure;
let bad_evs: Vec<_> = {
- let acousticness = ev_analysis.ev_acousticness.as_ref().expect("(bug) always computed!");
+ let acousticness = &ev_analysis.ev_acousticness.0;
izip!(1.., &evals, &evecs.0, &acousticness.0)
.take_while(|&(_, &freq, _, _)| freq < 0.0)
.filter(|&(_, _, _, &acousticness)| acousticness < 0.95)
@@ -224,7 +229,12 @@ impl TrialDir {
poscar::dump(self.create_file("final.vasp")?, "", &structure)?;
- write_eigen_info_for_machines(&ev_analysis, self.create_file("eigenvalues.final")?)?;
+ {
+ let mut file = self.create_file("eigenvalues.final")?;
+ ev_analysis.make_columns(ev_analyses::ColumnsMode::ForMachines)
+ .expect("(bug) no columns, not even frequency?")
+ .into_iter().map(|s| ok(writeln!(file, "{}", s)?)).collect::<Result<()>>()?;
+ }
// let (k_evals, k_evecs) = read_eigensystem(&bands_dir, &Q_K)?;
// let kinfos = get_k_eigensystem_info(
@@ -232,7 +242,10 @@ impl TrialDir {
// );
// write_summary_file(settings, &lmp, &einfos, &kinfos)?;
- self.write_summary_file(settings, &lmp, &ev_analysis)?;
+ {
+ let rest = ev_analysis.make_summary(settings);
+ self.write_summary_file(&lmp, rest)?;
+ }
})}
}
@@ -447,32 +460,12 @@ fn multi_threshold_deconstruct(
}
})}
-fn write_eigen_info_for_humans(
- analysis: &GammaSystemAnalysis,
- writeln: &mut FnMut(String) -> Result<()>,
-) -> Result<()>
-{
- analysis.make_columns(ev_analyses::ColumnsMode::ForHumans)
- .expect("(bug) no columns, not even frequency?")
- .into_iter().map(writeln).collect()
-}
-
-fn write_eigen_info_for_machines<W: Write>(
- analysis: &GammaSystemAnalysis,
- mut file: W,
-) -> Result<()>
-{
- analysis.make_columns(ev_analyses::ColumnsMode::ForMachines)
- .expect("(bug) no columns, not even frequency?")
- .into_iter().map(|s| ok(writeln!(file, "{}", s)?)).collect()
-}
impl TrialDir {
fn write_summary_file(
&self,
- settings: &Settings,
lmp: &LammpsBuilder,
- ev_analysis: &GammaSystemAnalysis,
+ rest: ::serde_yaml::Value,
) -> Result<()> {ok({
use ::ui::cfg_merging::{make_nested_mapping, no_summary, merge_summaries};
use ::rsp2_structure_io::poscar;
@@ -488,7 +481,7 @@ impl TrialDir {
// be done by saving structures into strongly typed objects
// for the analysis module
let mut out = vec![];
- out.push(ev_analysis.make_summary(settings));
+ out.push(rest);
out.push({
let f = |structure: ElementStructure| ok({
let na = structure.num_atoms() as f64;
diff --git a/src/tasks/lib.rs b/src/tasks/lib.rs
index 1a25cf4..11af448 100644
--- a/src/tasks/lib.rs
+++ b/src/tasks/lib.rs
@@ -22,6 +22,7 @@ extern crate rsp2_fs_util;
#[macro_use] extern crate rsp2_util_macros;
#[macro_use] extern crate rsp2_clap;
+#[macro_use] extern crate frunk;
#[macro_use] extern crate extension_trait;
extern crate rayon;
extern crate rand;
diff --git a/src/tasks/traits/alternate.rs b/src/tasks/traits/alternate.rs
index 0d83400..1a6e6a9 100644
--- a/src/tasks/traits/alternate.rs
+++ b/src/tasks/traits/alternate.rs
@@ -2,7 +2,7 @@
//! implemented manually on a type without unstable compiler features.
// These traits basically just let us use more functions in more places without
-// sacrificing too much DRYness, and without having to wait for existential
+// sacrificing too much DRY-ness, and without having to wait for existential
// types to get stabilized.
pub use ::std::prelude::v1::{
Fn as StdFn,
@@ -10,13 +10,20 @@ pub use ::std::prelude::v1::{
FnOnce as StdFnOnce,
};
+use ::std::ops::{Deref, DerefMut};
+use ::frunk::hlist::{HList, HNil, HCons};
+
/// Alternate `Fn` that can be manually implemented on stable.
///
/// It is automatically implemented for all types that implement
/// the standard library's `Fn`. The advantage is that it may
/// also be implemented for explicitly defined "unboxed closures".
-pub trait Fn<Args>: FnMut<Args> {
+pub trait Fn<Args: HList>: FnMut<Args> {
fn call(&self, args: Args) -> Self::Output;
+
+ /// Get a borrowed form of the closure.
+ #[inline(always)]
+ fn by_ref(&self) -> Ref<Self> { Ref(self) }
}
/// Alternate `FnMut` that can be manually implemented on stable.
@@ -24,29 +31,172 @@ pub trait Fn<Args>: FnMut<Args> {
/// It is automatically implemented for all types that implement
/// the standard library's `FnMut`. The advantage is that it may
/// also be implemented for explicitly defined "unboxed closures".
-pub trait FnMut<Args>: FnOnce<Args> {
+pub trait FnMut<Args: HList>: FnOnce<Args> {
fn call_mut(&mut self, args: Args) -> Self::Output;
+
+ /// Get a borrowed form of the closure.
+ #[inline(always)]
+ fn by_ref_mut(&mut self) -> RefMut<Self> { RefMut(self) }
}
+pub type CallT<F, Args> = <F as FnOnce<Args>>::Output;
+
/// Alternate FnOnce that can be manually implemented on stable.
///
/// It is automatically implemented for all types that implement
/// the standard library's `FnOnce`. The advantage is that it may
/// also be implemented for explicitly defined "unboxed closures".
-pub trait FnOnce<Args> {
+pub trait FnOnce<Args: HList> {
type Output;
+
fn call_once(self, args: Args) -> Self::Output;
}
-impl<F, R, Args> FnOnce<Args> for F where F: StdFnOnce(Args) -> R {
- type Output = R;
- fn call_once(self, args: Args) -> Self::Output { self(args) }
+// ---------------
+
+macro_rules! derive_fn_from_std {
+ ($([$([$a:ident : $A:ident])*])*) => {
+ $(
+ impl<F, R, $($A,)*> FnOnce<Hlist![$($A),*]> for F where F: StdFnOnce($($A),*) -> R {
+ type Output = R;
+
+ #[inline(always)]
+ fn call_once(self, hlist_pat![$($a),*]: Hlist![$($A),*]) -> Self::Output
+ { self($($a),*) }
+ }
+
+ impl<F, R, $($A,)*> FnMut<Hlist![$($A),*]> for F where F: StdFnMut($($A),*) -> R {
+ #[inline(always)]
+ fn call_mut(&mut self, hlist_pat![$($a),*]: Hlist![$($A),*]) -> Self::Output
+ { self($($a),*) }
+ }
+
+ impl<F, R, $($A,)*> Fn<Hlist![$($A),*]> for F where F: StdFn($($A),*) -> R {
+ #[inline(always)]
+ fn call(&self, hlist_pat![$($a),*]: Hlist![$($A),*]) -> Self::Output
+ { self($($a),*) }
+ }
+ )*
+ }
+}
+
+derive_fn_from_std!{
+ []
+ [[a0:A0]]
+ [[a0:A0][a1:A1]]
+ [[a0:A0][a1:A1][a2:A2]]
+ [[a0:A0][a1:A1][a2:A2][a3:A3]]
+ [[a0:A0][a1:A1][a2:A2][a3:A3][a4:A4]]
+ [[a0:A0][a1:A1][a2:A2][a3:A3][a4:A4][a5:A5]]
+ [[a0:A0][a1:A1][a2:A2][a3:A3][a4:A4][a5:A5][a6:A6]]
+ [[a0:A0][a1:A1][a2:A2][a3:A3][a4:A4][a5:A5][a6:A6][a7:A7]]
+ [[a0:A0][a1:A1][a2:A2][a3:A3][a4:A4][a5:A5][a6:A6][a7:A7][a8:A8]]
+ [[a0:A0][a1:A1][a2:A2][a3:A3][a4:A4][a5:A5][a6:A6][a7:A7][a8:A8][a9:A9]]
+ [[a0:A0][a1:A1][a2:A2][a3:A3][a4:A4][a5:A5][a6:A6][a7:A7][a8:A8][a9:A9][a10:A10]]
+ [[a0:A0][a1:A1][a2:A2][a3:A3][a4:A4][a5:A5][a6:A6][a7:A7][a8:A8][a9:A9][a10:A10][a11:A11]]
+}
+
+// ---------------
+
+pub trait Curry<A>: Sized {
+ // fn curry_ref(&self, a: A) -> Partial<Ref<Self>, A>;
+ // { self.by_ref().curry() }
+ // fn curry_mut(&mut self, a: A) -> Partial<RefMut<Self>, A>
+ // { self.by_ref_mut().curry() }
+ fn curry(self, a: A) -> Partial<Self, A>;
+}
+
+// NOTE: Unable to constrain F here.
+// You could write `F: FnOnce<HCons<A, Rest>>`, but where does Rest come from?
+impl<A, F> Curry<A> for F
+{
+ #[inline(always)]
+ fn curry(self, a: A) -> Partial<Self, A>
+ { Partial {
+ function: self,
+ arg: a,
+ }}
+}
+
+#[derive(Debug, Copy, Clone)]
+pub struct Partial<F, A> {
+ function: F,
+ arg: A,
+}
+
+impl<F, A, Rest: HList> FnOnce<Rest> for Partial<F, A>
+ where F: FnOnce<HCons<A, Rest>>
+{
+ type Output = F::Output;
+
+ #[inline(always)]
+ fn call_once(self, list: Rest) -> Self::Output
+ { self.function.call_once(list.prepend(self.arg)) }
+}
+
+// ---------------
+// Can't have generic impls for &F and &mut F thanks to the blanket impl,
+// so we'll work around it with newtypes.
+
+/// A borrowed form of an `alternate::Fn`.
+#[derive(Debug, Copy, Clone)]
+pub struct Ref<'a, F: ?Sized + 'a>(&'a F);
+
+/// A borrowed form of an `alternate::FnMut`.
+#[derive(Debug)]
+pub struct RefMut<'a, F: ?Sized + 'a>(&'a mut F);
+
+impl<'a, F: ?Sized> Deref for Ref<'a, F> {
+ type Target = F;
+
+ fn deref(&self) -> &Self::Target { &*self.0 }
+}
+
+impl<'a, F: ?Sized> Deref for RefMut<'a, F> {
+ type Target = F;
+
+ fn deref(&self) -> &Self::Target { &*self.0 }
}
-impl<F, R, Args> FnMut<Args> for F where F: StdFnMut(Args) -> R {
- fn call_mut(&mut self, args: Args) -> Self::Output { self(args) }
+impl<'a, F: ?Sized> DerefMut for RefMut<'a, F> {
+ fn deref_mut(&mut self) -> &mut Self::Target { &mut *self.0 }
+}
+
+impl<'a, F: ?Sized, Args: HList> FnOnce<Args> for Ref<'a, F>
+ where F: Fn<Args>,
+{
+ type Output = F::Output;
+
+ fn call_once(self, args: Args) -> Self::Output
+ { self.0.call(args) }
+}
+
+impl<'a, F: ?Sized, Args: HList> FnMut<Args> for Ref<'a, F>
+ where F: Fn<Args>,
+{
+ fn call_mut(&mut self, args: Args) -> Self::Output
+ { self.0.call(args) }
+}
+
+impl<'a, F: ?Sized, Args: HList> Fn<Args> for Ref<'a, F>
+ where F: Fn<Args>,
+{
+ fn call(&self, args: Args) -> Self::Output
+ { self.0.call(args) }
+}
+
+impl<'a, F: ?Sized, Args: HList> FnOnce<Args> for RefMut<'a, F>
+ where F: FnMut<Args>,
+{
+ type Output = F::Output;
+
+ fn call_once(mut self, args: Args) -> Self::Output
+ { self.0.call_mut(args) }
}
-impl<F, R, Args> Fn<Args> for F where F: StdFn(Args) -> R {
- fn call(&self, args: Args) -> Self::Output { self(args) }
+impl<'a, F: ?Sized, Args: HList> FnMut<Args> for RefMut<'a, F>
+ where F: FnMut<Args>,
+{
+ fn call_mut(&mut self, args: Args) -> Self::Output
+ { self.0.call_mut(args) }
}
diff --git a/src/tasks/traits/macros.rs b/src/tasks/traits/macros.rs
index 21086e6..94f6587 100644
--- a/src/tasks/traits/macros.rs
+++ b/src/tasks/traits/macros.rs
@@ -31,70 +31,81 @@ macro_rules! derive_filetype_wrapper {
///
/// This macro will automatically generate the impls for the traits
/// lower on the Fn hierarchy.
+///
+/// This is unsuitable for HOFs, which generally need different bounds
+/// (and *technically* different implementations) on each of the impls.
#[macro_export]
macro_rules! derive_alternate_fn {
- // FnOnce
- (
- impl[$($par:tt)*] FnOnce<$Arg:ty> for $Type:ty
- $([where $($bnd:tt)*])*
- { $($fn_once_body:tt)* }
- )
+ // multiple invocations - base case
+ () => {};
+
+ // enable Fn(A, B, C) sugar
+ (impl[$($par:tt)*] $Fn:ident($($Arg:ty),*) $($rest:tt)*)
=> {
- impl<$($par)*> $crate::alternate::FnOnce<$Arg> for $Type $(where $($bnd)*)*
- { $($fn_once_body)* }
+ derive_alternate_fn!{
+ impl[$($par)*] $Fn<($($Arg,)*)> $($rest)*
+ }
};
- // FnMut (+ FnOnce)
+ // multiple invocations - inductive case
(
- impl[$($par:tt)*] FnMut<$Arg:ty> for $Type:ty
+ impl[$($par:tt)*] $Fn:ident<$Arg:ty> for $Type:ty
$([where $($bnd:tt)*])*
{
- // explicitly captured so it can be moved to the FnOnce body
type Output = $Output:ty;
- $($fn_mut_body:tt)*
+ $($fn_def:tt)*
}
+ $($rest:tt)*
)
=> {
- impl<$($par)*> $crate::alternate::FnMut<$Arg> for $Type $(where $($bnd)*)*
- {
- $($fn_mut_body)*
+ derive_alternate_fn!{
+ @one($Fn) [$($par)*] [$Arg] [$Type] [$Output] [$($($bnd)*)*] [$($fn_def)*]
}
+ derive_alternate_fn!{ $($rest)* }
+ };
- impl<$($par)*> $crate::alternate::FnOnce<$Arg> for $Type $(where $($bnd)*)*
- {
+ // FnOnce
+ (@one(FnOnce) [$($par:tt)*] [$Arg:ty] [$Type:ty] [$Output:ty] [$($bnd:tt)*] [$($fn_def:tt)*])
+ => {
+ impl<$($par)*> $crate::traits::alternate::FnOnce<$Arg> for $Type where $($bnd)* {
type Output = $Output;
+
+ $($fn_def)*
+ }
+ };
+
+ // FnMut (+ FnOnce)
+ (@one(FnMut) [$($par:tt)*] [$Arg:ty] [$Type:ty] [$Output:ty] [$($bnd:tt)*] [$($fn_def:tt)*])
+ => {
+ impl<$($par)*> $crate::traits::alternate::FnMut<$Arg> for $Type where $($bnd)* {
+ $($fn_def)*
+ }
+
+ impl<$($par)*> $crate::traits::alternate::FnOnce<$Arg> for $Type where $($bnd)* {
+ type Output = $Output;
+
fn call_once(mut self, args: $Args) -> Self::Output
- { $crate::alternate::FnMut::call(&mut self, args) }
+ { $crate::traits::alternate::FnMut::call_mut(&mut self, args) }
}
};
// Fn (+ FnMut + FnOnce)
- (
- impl[$($par:tt)*] Fn<$Arg:ty> for $Type:ty
- $([where $($bnd:tt)*])*
- {
- // explicitly captured so it can be moved to the FnOnce body
- type Output = $Output:ty;
- $($fn_body:tt)*
- }
- )
+ (@one(Fn) [$($par:tt)*] [$Arg:ty] [$Type:ty] [$Output:ty] [$($bnd:tt)*] [$($fn_def:tt)*])
=> {
- impl<$($par)*> $crate::alternate::Fn<$Arg> for $Type $(where $($bnd)*)*
- {
- $($fn_body)*
+ impl<$($par)*> $crate::traits::alternate::Fn<$Arg> for $Type where $($bnd)* {
+ $($fn_def)*
}
- impl<$($par)*> $crate::alternate::FnMut<$Arg> for $Type $(where $($bnd)*)*
- {
+ impl<$($par)*> $crate::traits::alternate::FnMut<$Arg> for $Type where $($bnd)* {
fn call_mut(&mut self, args: $Arg) -> Self::Output
- { $crate::alternate::Fn::call(self, args) }
+ { $crate::traits::alternate::Fn::call(&*self, args) }
}
- impl<$($par)*> $crate::alternate::FnOnce<$Arg> for $Type $(where $($bnd)*)*
- {
+ impl<$($par)*> $crate::traits::alternate::FnOnce<$Arg> for $Type where $($bnd)* {
type Output = $Output;
+
fn call_once(self, args: $Arg) -> Self::Output
- { $crate::alternate::Fn::call(&self, args) }
+ { $crate::traits::alternate::Fn::call(&self, args) }
}
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment