Compare commits
43 Commits
2022-07-17
...
2022-07-20
Author | SHA1 | Date | |
---|---|---|---|
847b95f036 | |||
4cbcc46d50 | |||
727b7b43a3 | |||
50ae6d4c34 | |||
a8b6000104 | |||
fffe917c5c | |||
97ac46fd8a | |||
cdcc1fbbdd | |||
72a66dbff4 | |||
27c1523b0c | |||
491f863aea | |||
fcc735765c | |||
55e58f630c | |||
f2bb16eb5b | |||
19b1df4919 | |||
72b18d378f | |||
65f90d0654 | |||
e96f0db11a | |||
c889ec6d09 | |||
3541ab14c1 | |||
b8a36c87a6 | |||
fba85c5ae3 | |||
960804598a | |||
9153dfbb7a | |||
b15bad434e | |||
f448f9200e | |||
d6ebf968b2 | |||
90127e3f02 | |||
8ce64ecc73 | |||
86fb7f018d | |||
2e3021b875 | |||
b555ee93f0 | |||
d034453970 | |||
5b5085829d | |||
7dd4b09faf | |||
24b0b3d680 | |||
d0ae25e28b | |||
e029c8b3d9 | |||
5ddd6fef74 | |||
0ce862b614 | |||
415ffb9c4d | |||
dfe27c9b56 | |||
00ae71a6eb |
@@ -59,7 +59,7 @@ fn main() {
|
||||
let coupling_region = Torus::new_xz(Meters::new(0.5*(ferro1_center + ferro2_center), ferro_center_y, half_depth), wire_coupling_major, wire_minor);
|
||||
let sense_region = Torus::new_xz(Meters::new(ferro2_center + ferro_major, ferro_center_y, half_depth), wire_major, wire_minor);
|
||||
|
||||
let mut driver: SpirvDriver<spirv::FullyGenericMaterial> = Driver::new_spirv(Meters::new(width, height, depth), feat_size);
|
||||
let mut driver: SpirvDriver<mat::FullyGenericMaterial<f32>> = Driver::new_spirv(Meters::new(width, height, depth), feat_size);
|
||||
|
||||
// mu_r=881.33, starting at H=25 to H=75.
|
||||
driver.fill_region(&ferro1_region, mat::MHPgram::new(25.0, 881.33, 44000.0));
|
||||
|
@@ -36,7 +36,7 @@ pub struct Driver<S=SimState> {
|
||||
}
|
||||
|
||||
pub type CpuDriver<R=real::R32, M=mat::GenericMaterial<R>> = Driver<SimState<R, M>>;
|
||||
pub type SpirvDriver<M=spirv::FullyGenericMaterial> = Driver<SpirvSim<M>>;
|
||||
pub type SpirvDriver<M=mat::FullyGenericMaterial<f32>> = Driver<SpirvSim<M>>;
|
||||
|
||||
impl<R: Real, M: Default> Driver<SimState<R, M>> {
|
||||
pub fn new<C: Coord>(size: C, feature_size: f32) -> Self {
|
||||
|
@@ -13,7 +13,14 @@ mod linear;
|
||||
|
||||
pub use bh_ferromagnet::*;
|
||||
pub use mb_ferromagnet::*;
|
||||
pub use coremem_types::mat::{AnisomorphicConductor, Ferroxcube3R1MH, IsoConductorOr, IsomorphicConductor, MHPgram};
|
||||
pub use coremem_types::mat::{
|
||||
AnisomorphicConductor,
|
||||
Ferroxcube3R1MH,
|
||||
FullyGenericMaterial,
|
||||
IsoConductorOr,
|
||||
IsomorphicConductor,
|
||||
MHPgram
|
||||
};
|
||||
pub use linear::*;
|
||||
|
||||
#[enum_dispatch]
|
||||
|
@@ -2,7 +2,18 @@ use serde::de::Deserializer;
|
||||
use serde::ser::Serializer;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::mat::{AnisomorphicConductor, IsoConductorOr, IsomorphicConductor, Ferroxcube3R1MH, MaterialExt as _, MBFerromagnet, MBPgram, MHPgram, Static};
|
||||
use crate::mat::{
|
||||
AnisomorphicConductor,
|
||||
IsoConductorOr,
|
||||
IsomorphicConductor,
|
||||
Ferroxcube3R1MH,
|
||||
FullyGenericMaterial,
|
||||
MaterialExt as _,
|
||||
MBFerromagnet,
|
||||
MBPgram,
|
||||
MHPgram,
|
||||
Static
|
||||
};
|
||||
use crate::geom::{Index, Vec3, Vec3u};
|
||||
|
||||
/// hide the actual spirv backend structures inside a submodule to make their use/boundary clear.
|
||||
@@ -10,7 +21,6 @@ mod ffi {
|
||||
pub use spirv_backend::entry_points;
|
||||
pub use spirv_backend::sim::SerializedSimMeta;
|
||||
pub use spirv_backend::support::Optional;
|
||||
pub use spirv_backend::mat::FullyGenericMaterial;
|
||||
pub use coremem_types::mat::MBPgram;
|
||||
}
|
||||
|
||||
@@ -88,86 +98,19 @@ impl IntoLib for ffi::MBPgram<f32> {
|
||||
|
||||
identity!( => MHPgram<f32>);
|
||||
identity!( => Ferroxcube3R1MH);
|
||||
identity!( => FullyGenericMaterial<f32>);
|
||||
identity!(R, M, => IsoConductorOr<R, M>);
|
||||
|
||||
#[derive(Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||
pub struct FullyGenericMaterial {
|
||||
pub conductivity: Vec3<f32>,
|
||||
pub m_b_curve: Option<MBPgram<f32>>,
|
||||
pub m_h_curve: Option<MHPgram<f32>>,
|
||||
}
|
||||
|
||||
impl IntoFfi for FullyGenericMaterial {
|
||||
type Ffi = ffi::FullyGenericMaterial;
|
||||
fn into_ffi(self) -> Self::Ffi {
|
||||
Self::Ffi {
|
||||
conductivity: self.conductivity.into_ffi(),
|
||||
m_b_curve: self.m_b_curve.into_ffi(),
|
||||
m_h_curve: self.m_h_curve.into_ffi(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoLib for ffi::FullyGenericMaterial {
|
||||
type Lib = FullyGenericMaterial;
|
||||
fn into_lib(self) -> Self::Lib {
|
||||
Self::Lib {
|
||||
conductivity: self.conductivity.into_lib(),
|
||||
m_b_curve: self.m_b_curve.into_lib(),
|
||||
m_h_curve: self.m_h_curve.into_lib(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Static<f32>> for FullyGenericMaterial {
|
||||
// N.B.: this isn't fully correct, as Static also encodes the magnetic component
|
||||
impl From<Static<f32>> for FullyGenericMaterial<f32> {
|
||||
fn from(m: Static<f32>) -> Self {
|
||||
FullyGenericMaterial {
|
||||
conductivity: m.conductivity(),
|
||||
.. Default::default()
|
||||
}
|
||||
AnisomorphicConductor::new(m.conductivity).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AnisomorphicConductor<f32>> for FullyGenericMaterial {
|
||||
fn from(m: AnisomorphicConductor<f32>) -> Self {
|
||||
FullyGenericMaterial {
|
||||
conductivity: m.conductivity(),
|
||||
.. Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IsomorphicConductor<f32>> for FullyGenericMaterial {
|
||||
fn from(m: IsomorphicConductor<f32>) -> Self {
|
||||
FullyGenericMaterial {
|
||||
conductivity: m.conductivity(),
|
||||
.. Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MBFerromagnet<f32>> for FullyGenericMaterial {
|
||||
impl From<MBFerromagnet<f32>> for FullyGenericMaterial<f32> {
|
||||
fn from(m: MBFerromagnet<f32>) -> Self {
|
||||
FullyGenericMaterial {
|
||||
m_b_curve: Some(m.curve()),
|
||||
.. Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MHPgram<f32>> for FullyGenericMaterial {
|
||||
fn from(m: MHPgram<f32>) -> Self {
|
||||
FullyGenericMaterial {
|
||||
m_h_curve: Some(m),
|
||||
.. Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Ferroxcube3R1MH> for FullyGenericMaterial {
|
||||
fn from(m: Ferroxcube3R1MH) -> Self {
|
||||
let curve: MHPgram<f32> = m.into();
|
||||
curve.into()
|
||||
m.curve().into_ffi().into()
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -12,16 +12,16 @@ use crate::geom::{Coord, Index, Meters, Vec3};
|
||||
use crate::real::Real as _;
|
||||
use crate::sim::{CellStateWithM, GenericSim, MaterialSim, Sample, SampleableSim};
|
||||
use crate::stim::AbstractStimulus;
|
||||
use coremem_types::mat::Material;
|
||||
use coremem_types::mat::{FullyGenericMaterial, Material};
|
||||
|
||||
mod bindings;
|
||||
pub use bindings::{entry_points, IntoFfi, IntoLib, FullyGenericMaterial, Remote, SimMeta};
|
||||
pub use bindings::{entry_points, IntoFfi, IntoLib, Remote, SimMeta};
|
||||
|
||||
/// Wrapper around an inner state object which offloads stepping onto a spirv backend (e.g. GPU).
|
||||
#[derive(Clone, Default, Serialize, Deserialize)]
|
||||
#[serde(bound(serialize = "M: Serialize + IntoFfi, M::Ffi: Clone + IntoLib<Lib=M>"))]
|
||||
#[serde(bound(deserialize = "M: Deserialize<'de> + IntoFfi, M::Ffi: IntoLib<Lib=M>"))]
|
||||
pub struct SpirvSim<M=FullyGenericMaterial>
|
||||
pub struct SpirvSim<M=FullyGenericMaterial<f32>>
|
||||
where M: IntoFfi,
|
||||
{
|
||||
meta: SimMeta,
|
||||
@@ -70,7 +70,7 @@ impl WgpuData {
|
||||
|
||||
impl Default for WgpuData {
|
||||
fn default() -> Self {
|
||||
Self::new::<FullyGenericMaterial>(0)
|
||||
Self::new::<FullyGenericMaterial<f32>>(0)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,29 +1,4 @@
|
||||
use crate::support::Optional;
|
||||
use coremem_types::mat::{Material, MBPgram, MHPgram};
|
||||
use coremem_types::vec::Vec3;
|
||||
|
||||
|
||||
#[derive(Copy, Clone, Default, PartialEq)]
|
||||
pub struct FullyGenericMaterial {
|
||||
pub conductivity: Vec3<f32>,
|
||||
pub m_b_curve: Optional<MBPgram<f32>>,
|
||||
pub m_h_curve: Optional<MHPgram<f32>>,
|
||||
}
|
||||
|
||||
impl Material<f32> for FullyGenericMaterial {
|
||||
fn conductivity(&self) -> Vec3<f32> {
|
||||
self.conductivity
|
||||
}
|
||||
fn move_b_vec(&self, m: Vec3<f32>, target_b: Vec3<f32>) -> Vec3<f32> {
|
||||
if self.m_b_curve.is_some() {
|
||||
self.m_b_curve.unwrap().move_b_vec(m, target_b)
|
||||
} else if self.m_h_curve.is_some() {
|
||||
self.m_h_curve.unwrap().move_b_vec(m, target_b)
|
||||
} else {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: plumb the R parameter through and remove this.
|
||||
pub type FullyGenericMaterial = coremem_types::mat::FullyGenericMaterial<f32>;
|
||||
pub type IsoConductorOr<M> = coremem_types::mat::IsoConductorOr<f32, M>;
|
||||
|
||||
|
370
crates/types/src/compound/enumerated.rs
Normal file
370
crates/types/src/compound/enumerated.rs
Normal file
@@ -0,0 +1,370 @@
|
||||
use crate::compound::peano::{P0, Peano, PNext};
|
||||
use crate::compound::list::{self, Indexable, IntoList, List};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
/// implement for something which supports being called for this specific variant
|
||||
pub trait VariantHandler<N: Peano, Arg, Output> {
|
||||
fn call(self, a: Arg) -> Output;
|
||||
}
|
||||
|
||||
/// anything which can encode a discriminant up to *but not including* P
|
||||
pub trait DiscriminantCodable<P: Peano>: Sized {
|
||||
fn decode_discr(&self) -> Discr<P>;
|
||||
fn encode_discr(d: Discr<P>) -> Self;
|
||||
fn set_discr(&mut self, d: Discr<P>) {
|
||||
*self = Self::encode_discr(d)
|
||||
}
|
||||
}
|
||||
|
||||
/// discriminant which encodes up to *but not including* P.
|
||||
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
||||
#[cfg_attr(feature = "fmt", derive(Debug))]
|
||||
#[derive(Copy, Clone, Default, PartialEq)]
|
||||
pub struct Discr<P: Peano>(u32, P::Unit);
|
||||
|
||||
impl<P: Peano> DiscriminantCodable<P> for Discr<P> {
|
||||
fn decode_discr(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
fn encode_discr(d: Discr<P>) -> Self {
|
||||
d
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Peano> Discr<P> {
|
||||
pub fn new(u: u32) -> Self {
|
||||
assert!(u < P::VALUE);
|
||||
Self::new_unchecked(u)
|
||||
}
|
||||
pub fn value(&self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
fn new_unchecked(u: u32) -> Self {
|
||||
Self(u, Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait DiscrDispatch<P: Peano> {
|
||||
fn dispatch<H: DiscrHandler<P, Output>, Output>(&self, h: H) -> Output;
|
||||
}
|
||||
|
||||
impl<P: Peano> DiscrDispatch<PNext<P>> for Discr<PNext<P>>
|
||||
where
|
||||
Discr<P>: DiscrDispatch<P>
|
||||
{
|
||||
fn dispatch<H: DiscrHandler<PNext<P>, O>, O>(&self, h: H) -> O {
|
||||
match self.value() {
|
||||
// consider P=2: we want to dispatch values of 0 and 1, but handle 2:
|
||||
// so dispatch v < P
|
||||
v if v < P::VALUE => Discr::<P>::new_unchecked(v).dispatch(h.prev()),
|
||||
v if v == P::VALUE => h.call(),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DiscrDispatch<P0> for Discr<P0> {
|
||||
fn dispatch<H: DiscrHandler<P0, O>, O>(&self, h: H) -> O {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
/// something which can be called with any value up to (but not including) N
|
||||
pub trait DiscrHandler<N: Peano, R> {
|
||||
type PrevOrPanic: DiscrHandler<N::PrevOrZero, R>;
|
||||
/// called when the discriminant has value N-1
|
||||
fn call(self) -> R;
|
||||
/// discriminant is < N-1: dispatch to the next handler.
|
||||
/// in the case that N = 1, this path *should* be unreachable,
|
||||
/// so a panic would be allowed.
|
||||
fn prev(self) -> Self::PrevOrPanic;
|
||||
}
|
||||
|
||||
/// helper used to call F with some (yet-to-be-determined) index of I
|
||||
pub struct DispatchIndexable<I, F> {
|
||||
indexable: I,
|
||||
f: F,
|
||||
}
|
||||
|
||||
impl<I, F> DispatchIndexable<I, F> {
|
||||
fn new(indexable: I, f: F) -> Self {
|
||||
Self { indexable, f }
|
||||
}
|
||||
}
|
||||
|
||||
// base case: we tried to index all cases >= 0 and failed.
|
||||
// this should be unreachable.
|
||||
impl<'a, I, F, R> DiscrHandler<P0, R> for DispatchIndexable<I, F>
|
||||
{
|
||||
type PrevOrPanic = Self;
|
||||
fn call(self) -> R {
|
||||
unreachable!()
|
||||
}
|
||||
fn prev(self) -> Self::PrevOrPanic {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
// inductive case: if we know how dispatch up through P, and the collection is P+1-indexable, then we can
|
||||
// index up through P+1
|
||||
impl<'a, I, F, P: Peano, R> DiscrHandler<PNext<P>, R> for DispatchIndexable<&'a I, F>
|
||||
where
|
||||
I: Indexable<P>,
|
||||
I::Element: Copy,
|
||||
F: VariantHandler<P, I::Element, R>,
|
||||
Self: DiscrHandler<P, R>,
|
||||
{
|
||||
type PrevOrPanic = Self;
|
||||
fn call(self) -> R {
|
||||
self.f.call(self.indexable.get())
|
||||
}
|
||||
fn prev(self) -> Self::PrevOrPanic {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// mutable indexing case: if we have a mutable handle to the Indexable,
|
||||
// then assume the variants want to have mutable references to the items.
|
||||
impl<'a, I, F, P: Peano, R> DiscrHandler<PNext<P>, R> for DispatchIndexable<&'a mut I, F>
|
||||
where
|
||||
I: Indexable<P>,
|
||||
I::Element: 'a,
|
||||
F: VariantHandler<P, &'a mut I::Element, R>,
|
||||
Self: DiscrHandler<P, R>,
|
||||
{
|
||||
type PrevOrPanic = Self;
|
||||
fn call(self) -> R {
|
||||
self.f.call(self.indexable.get_mut())
|
||||
}
|
||||
fn prev(self) -> Self::PrevOrPanic {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
||||
#[cfg_attr(feature = "fmt", derive(Debug))]
|
||||
#[derive(Copy, Clone, Default, PartialEq)]
|
||||
pub struct Enum<D, L>(D, L);
|
||||
|
||||
pub type InternallyDiscriminated<Args> = Enum<(), List<Args>>;
|
||||
|
||||
impl<P: Peano, L> Enum<(Discr<P>,), L> {
|
||||
pub fn new<Variants>(v: Variants) -> Self
|
||||
where
|
||||
Variants: IntoList<List=L>,
|
||||
L: list::Meta<Length=P>,
|
||||
{
|
||||
Enum((Discr::default(),), v.into_list())
|
||||
}
|
||||
}
|
||||
impl<L> Enum<(), L> {
|
||||
pub fn internally_discriminated<Variants>(v: Variants) -> Self
|
||||
where
|
||||
Variants: IntoList<List=L>,
|
||||
{
|
||||
Enum((), v.into_list())
|
||||
}
|
||||
}
|
||||
|
||||
impl<D, L> Enum<D, L> {
|
||||
/// use with care. long term, this probably shouldn't stay public.
|
||||
pub fn from_components(discr: D, list: L) -> Self {
|
||||
Self(discr, list)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait EnumRequirements {
|
||||
type NumVariants: Peano;
|
||||
fn decode_discr(&self) -> Discr<Self::NumVariants>;
|
||||
fn encode_discr(&mut self, d: Discr<Self::NumVariants>);
|
||||
}
|
||||
|
||||
impl<D, L> EnumRequirements for Enum<(D,), L>
|
||||
where
|
||||
D: DiscriminantCodable<<L as list::Meta>::Length>,
|
||||
L: list::Meta,
|
||||
{
|
||||
type NumVariants = <L as list::Meta>::Length;
|
||||
fn decode_discr(&self) -> Discr<Self::NumVariants> {
|
||||
self.0.0.decode_discr()
|
||||
}
|
||||
fn encode_discr(&mut self, d: Discr<Self::NumVariants>) {
|
||||
self.0.0.set_discr(d)
|
||||
}
|
||||
}
|
||||
|
||||
impl<L> EnumRequirements for Enum<(), L>
|
||||
where
|
||||
L: list::Meta + Indexable<P0>,
|
||||
list::ElementAt<P0, L>: DiscriminantCodable<<L as list::Meta>::Length>,
|
||||
{
|
||||
type NumVariants = <L as list::Meta>::Length;
|
||||
fn decode_discr(&self) -> Discr<Self::NumVariants> {
|
||||
self.1.get_ref().decode_discr()
|
||||
}
|
||||
fn encode_discr(&mut self, d: Discr<Self::NumVariants>) {
|
||||
self.1.get_mut().set_discr(d)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<D, L> Enum<D, L>
|
||||
where
|
||||
Self: EnumRequirements
|
||||
{
|
||||
/// invoke the closure on the active variant, passing the variant by-value
|
||||
pub fn dispatch<'a, F, R>(&'a self, f: F) -> R
|
||||
where
|
||||
DispatchIndexable<&'a L, F>: DiscrHandler<<Self as EnumRequirements>::NumVariants, R>,
|
||||
// TODO: this trait bound shouldn't be necessary. Discr ALWAYS implements DiscrDispatch
|
||||
Discr<<Self as EnumRequirements>::NumVariants>: DiscrDispatch<<Self as EnumRequirements>::NumVariants>,
|
||||
{
|
||||
self.decode_discr().dispatch(DispatchIndexable::new(&self.1, f))
|
||||
}
|
||||
|
||||
/// invoke the closure on the active variant, passing the variant by mutable reference
|
||||
pub fn dispatch_mut<'a, F, R>(&'a mut self, f: F) -> R
|
||||
where
|
||||
DispatchIndexable<&'a mut L, F>: DiscrHandler<<Self as EnumRequirements>::NumVariants, R>,
|
||||
Discr<<Self as EnumRequirements>::NumVariants>: DiscrDispatch<<Self as EnumRequirements>::NumVariants>,
|
||||
{
|
||||
self.decode_discr().dispatch(DispatchIndexable::new(&mut self.1, f))
|
||||
}
|
||||
|
||||
pub fn set<P>(&mut self, value: L::Element)
|
||||
where
|
||||
P: Peano,
|
||||
L: Indexable<P>,
|
||||
{
|
||||
self.encode_discr(Discr::new(P::VALUE));
|
||||
self.1.set(value);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::compound::peano::{P1, P2, P3};
|
||||
use crate::compound::list::List;
|
||||
|
||||
struct ReadReceiver;
|
||||
impl<P: Peano, T: TryInto<i32>> VariantHandler<P, T, i32> for ReadReceiver {
|
||||
fn call(self, v: T) -> i32 {
|
||||
unsafe {
|
||||
v.try_into().unwrap_unchecked()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct AddIndexPlus5Receiver;
|
||||
impl<P: Peano, T: TryInto<i32>> VariantHandler<P, T, i32> for AddIndexPlus5Receiver {
|
||||
fn call(self, v: T) -> i32 {
|
||||
unsafe {
|
||||
v.try_into().unwrap_unchecked() + P::VALUE as i32 + 5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Add4Receiver;
|
||||
impl<P: Peano, T: TryInto<i32> + TryFrom<i32> + Copy> VariantHandler<P, &mut T, ()> for Add4Receiver {
|
||||
fn call(self, v: &mut T) -> () {
|
||||
unsafe {
|
||||
*v = ((*v).try_into().unwrap_unchecked() + 4).try_into().unwrap_unchecked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dispatch() {
|
||||
let mut e: Enum<(Discr<P3>,), List<(u32, i32, u8)>> = Enum::default();
|
||||
assert_eq!(e.dispatch(AddIndexPlus5Receiver), 5);
|
||||
|
||||
e.encode_discr(Discr::new(1));
|
||||
assert_eq!(e.dispatch(AddIndexPlus5Receiver), 6);
|
||||
|
||||
e.encode_discr(Discr::new(2));
|
||||
assert_eq!(e.dispatch(AddIndexPlus5Receiver), 7);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dispatch_mut() {
|
||||
let mut e: Enum<(Discr<P3>,), List<(u32, i32, u8)>> = Enum::default();
|
||||
e.dispatch_mut(Add4Receiver);
|
||||
assert_eq!(e.dispatch(AddIndexPlus5Receiver), 9);
|
||||
|
||||
e.encode_discr(Discr::new(1));
|
||||
assert_eq!(e.dispatch(AddIndexPlus5Receiver), 6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set() {
|
||||
let mut e: Enum<(Discr<P3>,), List<(u32, i32, u8)>> = Enum::default();
|
||||
e.set::<P0>(2u32);
|
||||
assert_eq!(e.dispatch(ReadReceiver), 2);
|
||||
|
||||
e.set::<P1>(3i32);
|
||||
assert_eq!(e.dispatch(ReadReceiver), 3);
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Default, PartialEq)]
|
||||
struct BoxedF32(f32);
|
||||
|
||||
impl Into<i32> for BoxedF32 {
|
||||
fn into(self) -> i32 {
|
||||
self.0 as i32
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for BoxedF32 {
|
||||
fn from(v: i32) -> Self {
|
||||
Self(v as f32)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Peano> DiscriminantCodable<P> for BoxedF32 {
|
||||
fn decode_discr(&self) -> Discr<P> {
|
||||
match self.0 {
|
||||
v if v < 0f32 => Discr::new((-v) as u32),
|
||||
_non_negative => Discr::new(0),
|
||||
}
|
||||
}
|
||||
fn encode_discr(d: Discr<P>) -> Self {
|
||||
Self(-(d.value() as f32))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn internal_discr() {
|
||||
type E = Enum<(), List<(BoxedF32, i32, u8)>>;
|
||||
assert_eq!(<E as EnumRequirements>::NumVariants::VALUE, 3);
|
||||
|
||||
let mut e: E = Enum::default();
|
||||
assert_eq!(e.dispatch(ReadReceiver), 0);
|
||||
|
||||
e.set::<P0>(BoxedF32(16f32));
|
||||
assert_eq!(e.dispatch(ReadReceiver), 16);
|
||||
|
||||
e.set::<P1>(5);
|
||||
assert_eq!(e.dispatch(ReadReceiver), 5);
|
||||
|
||||
e.set::<P2>(8);
|
||||
assert_eq!(e.dispatch(ReadReceiver), 8);
|
||||
|
||||
e.set::<P0>(BoxedF32(0f32));
|
||||
assert_eq!(e.dispatch(ReadReceiver), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new() {
|
||||
type E = Enum<(Discr<P2>,), List<(u32, i32)>>;
|
||||
assert_eq!(<E as EnumRequirements>::NumVariants::VALUE, 2);
|
||||
|
||||
let e: E = Enum::new((5u32, 4i32));
|
||||
assert_eq!(e.dispatch(ReadReceiver), 5);
|
||||
|
||||
let e = Enum::internally_discriminated((BoxedF32(4f32), -1i32));
|
||||
assert_eq!(e.dispatch(ReadReceiver), 4);
|
||||
}
|
||||
}
|
@@ -1,300 +0,0 @@
|
||||
use crate::compound::peano::{P0, Peano, PNext};
|
||||
|
||||
#[derive(Copy, Clone, Default, PartialEq)]
|
||||
struct Null;
|
||||
|
||||
#[derive(Copy, Clone, Default, PartialEq)]
|
||||
struct LLNode<E, N> {
|
||||
e: E,
|
||||
// Null or LLNode
|
||||
next: N,
|
||||
}
|
||||
|
||||
impl<E> LLNode<E, Null> {
|
||||
fn new(e: E) -> Self {
|
||||
Self {
|
||||
e,
|
||||
next: Null,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, N> LLNode<E, N> {
|
||||
fn prepend<P>(self, e: P) -> LLNode<P, Self> {
|
||||
LLNode {
|
||||
e,
|
||||
next: self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ListBuilder<ElemTypes>(core::marker::PhantomData<ElemTypes>);
|
||||
type List<ElemTypes> = <ListBuilder<ElemTypes> as ListBuilderOps>::Result;
|
||||
|
||||
trait ListBuilderOps {
|
||||
type Result;
|
||||
}
|
||||
|
||||
impl<E0> ListBuilderOps for ListBuilder<(E0,)> {
|
||||
type Result = LLNode<E0, Null>;
|
||||
}
|
||||
impl<E0, E1> ListBuilderOps for ListBuilder<(E0, E1)> {
|
||||
type Result = LLNode<E0, List<(E1,)>>;
|
||||
}
|
||||
impl<E0, E1, E2> ListBuilderOps for ListBuilder<(E0, E1, E2)> {
|
||||
type Result = LLNode<E0, List<(E1, E2)>>;
|
||||
}
|
||||
impl<E0, E1, E2, E3> ListBuilderOps for ListBuilder<(E0, E1, E2, E3)> {
|
||||
type Result = LLNode<E0, List<(E1, E2, E3)>>;
|
||||
}
|
||||
impl<E0, E1, E2, E3, E4> ListBuilderOps for ListBuilder<(E0, E1, E2, E3, E4)> {
|
||||
type Result = LLNode<E0, List<(E1, E2, E3, E4)>>;
|
||||
}
|
||||
impl<E0, E1, E2, E3, E4, E5> ListBuilderOps for ListBuilder<(E0, E1, E2, E3, E4, E5)> {
|
||||
type Result = LLNode<E0, List<(E1, E2, E3, E4, E5)>>;
|
||||
}
|
||||
impl<E0, E1, E2, E3, E4, E5, E6> ListBuilderOps for ListBuilder<(E0, E1, E2, E3, E4, E5, E6)> {
|
||||
type Result = LLNode<E0, List<(E1, E2, E3, E4, E5, E6)>>;
|
||||
}
|
||||
impl<E0, E1, E2, E3, E4, E5, E6, E7> ListBuilderOps for ListBuilder<(E0, E1, E2, E3, E4, E5, E6, E7)> {
|
||||
type Result = LLNode<E0, List<(E1, E2, E3, E4, E5, E6, E7)>>;
|
||||
}
|
||||
|
||||
/// implemented on a list to allow applying some function `F` to any (or all) elements of the list.
|
||||
trait ListOp<F, List, Output> {
|
||||
/// apply `f` to all elements in the list.
|
||||
fn apply_all(&self, f: F);
|
||||
/// apply `f` *only* to the element at the provided index
|
||||
fn apply_at(&self, i: u32, f: F) -> Output {
|
||||
self.apply_at_rel(i, 0, f)
|
||||
}
|
||||
// helper: tracks the current index into the list to know when to call `f`
|
||||
fn apply_at_rel(&self, i: u32, cur: u32, f: F) -> Output;
|
||||
}
|
||||
|
||||
/// some operation which can be applied to the provided element type `T`.
|
||||
trait ElementOp<T> {
|
||||
type Output;
|
||||
/// perform the operation on the provided element.
|
||||
fn call(&self, el: &T) -> Self::Output;
|
||||
}
|
||||
|
||||
impl<T, R, F: Fn(&T) -> R> ElementOp<T> for F {
|
||||
type Output = R;
|
||||
fn call(&self, el: &T) -> Self::Output {
|
||||
(self)(el)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Output, F, E, N> ListOp<F, LLNode<E, N>, Output> for LLNode<E, N>
|
||||
where
|
||||
F: ElementOp<E, Output=Output>,
|
||||
N: ListOp<F, N, Output>
|
||||
{
|
||||
fn apply_all(&self, f: F) {
|
||||
// apply the operation to our element
|
||||
f.call(&self.e);
|
||||
// apply the operation to all tail elements
|
||||
self.next.apply_all(f);
|
||||
}
|
||||
fn apply_at_rel(&self, i: u32, cur: u32, f: F) -> Output {
|
||||
if i == cur {
|
||||
f.call(&self.e)
|
||||
} else {
|
||||
self.next.apply_at_rel(i, cur + 1, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Output, F> ListOp<F, Null, Output> for Null {
|
||||
fn apply_all(&self, _f: F) {
|
||||
// the tail element simply terminates recursion
|
||||
}
|
||||
fn apply_at_rel(&self, _i: u32, _cur: u32, _f: F) -> Output {
|
||||
// it's expected that the element was found by now
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
/// implemented on a list AND index, allowing potentially non-generic code to fetch a
|
||||
/// compile-time-constant element.
|
||||
trait IndexOp<List, Index, Elem> {
|
||||
fn get(&self, idx: Index) -> &Elem;
|
||||
}
|
||||
|
||||
/// zero-index into Self
|
||||
impl<E, N> IndexOp<LLNode<E, N>, P0, E> for LLNode<E, N>
|
||||
{
|
||||
fn get(&self, _idx: P0) -> &E {
|
||||
&self.e
|
||||
}
|
||||
}
|
||||
|
||||
/// index N+1 into Self given that we know how to index N into Self::next
|
||||
impl<E, N, P, ElemAtP> IndexOp<LLNode<E, N>, PNext<P>, ElemAtP> for LLNode<E, N>
|
||||
where
|
||||
P: Peano,
|
||||
N: IndexOp<N, P, ElemAtP>
|
||||
{
|
||||
fn get(&self, _idx: PNext<P>) -> &ElemAtP {
|
||||
self.next.get(P::default())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::compound::peano::{P1, P2};
|
||||
use core::cell::Cell;
|
||||
|
||||
#[derive(Default)]
|
||||
struct ApplyAccumulator<T> {
|
||||
delayed0: Cell<T>,
|
||||
delayed1: Cell<T>,
|
||||
delayed2: Cell<T>,
|
||||
}
|
||||
|
||||
impl ElementOp<u32> for &ApplyAccumulator<u32> {
|
||||
type Output = ();
|
||||
fn call(&self, el: &u32) {
|
||||
self.delayed2.set(
|
||||
self.delayed1.replace(
|
||||
self.delayed0.replace(*el)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_apply_single() {
|
||||
let list = LLNode::new(5u32);
|
||||
let acc = ApplyAccumulator::default();
|
||||
list.apply_all(&acc);
|
||||
|
||||
assert_eq!(acc.delayed1.into_inner(), 0);
|
||||
assert_eq!(acc.delayed0.into_inner(), 5);
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Default, Debug, PartialEq)]
|
||||
struct V0(u32);
|
||||
#[derive(Copy, Clone, Default, Debug, PartialEq)]
|
||||
struct V1(f32);
|
||||
#[derive(Copy, Clone, Default, Debug, PartialEq)]
|
||||
struct Flat {
|
||||
v0: V0,
|
||||
v1: V1,
|
||||
}
|
||||
|
||||
trait Variant {
|
||||
fn to_flat(&self) -> Flat;
|
||||
}
|
||||
impl Variant for V0 {
|
||||
fn to_flat(&self) -> Flat {
|
||||
Flat { v0: *self, ..Default::default() }
|
||||
}
|
||||
}
|
||||
impl Variant for V1 {
|
||||
fn to_flat(&self) -> Flat {
|
||||
Flat { v1: *self, ..Default::default() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Variant> ElementOp<T> for &ApplyAccumulator<Flat> {
|
||||
type Output = ();
|
||||
fn call(&self, el: &T) {
|
||||
self.delayed2.set(
|
||||
self.delayed1.replace(
|
||||
self.delayed0.replace(el.to_flat())
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_apply_2() {
|
||||
let list = LLNode::new(V0(5u32))
|
||||
.prepend(V1(4f32));
|
||||
let acc = ApplyAccumulator::default();
|
||||
list.apply_all(&acc);
|
||||
|
||||
assert_eq!(acc.delayed2.into_inner(), Flat::default());
|
||||
assert_eq!(acc.delayed1.into_inner(), V1(4f32).to_flat());
|
||||
assert_eq!(acc.delayed0.into_inner(), V0(5u32).to_flat());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_apply_3() {
|
||||
let list = LLNode::new(V0(5u32))
|
||||
.prepend(V1(4f32))
|
||||
.prepend(V0(2u32));
|
||||
let acc = ApplyAccumulator::default();
|
||||
list.apply_all(&acc);
|
||||
|
||||
assert_eq!(acc.delayed2.into_inner(), V0(2u32).to_flat());
|
||||
assert_eq!(acc.delayed1.into_inner(), V1(4f32).to_flat());
|
||||
assert_eq!(acc.delayed0.into_inner(), V0(5u32).to_flat());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_apply_at() {
|
||||
let list = LLNode::new(V0(5u32))
|
||||
.prepend(V1(4f32))
|
||||
.prepend(V0(2u32));
|
||||
|
||||
let acc = ApplyAccumulator::default();
|
||||
list.apply_at(0, &acc);
|
||||
assert_eq!(acc.delayed0.into_inner(), V0(2u32).to_flat());
|
||||
assert_eq!(acc.delayed1.into_inner(), Flat::default());
|
||||
|
||||
let acc = ApplyAccumulator::default();
|
||||
list.apply_at(1, &acc);
|
||||
assert_eq!(acc.delayed0.into_inner(), V1(4f32).to_flat());
|
||||
assert_eq!(acc.delayed1.into_inner(), Flat::default());
|
||||
|
||||
let acc = ApplyAccumulator::default();
|
||||
list.apply_at(2, &acc);
|
||||
assert_eq!(acc.delayed0.into_inner(), V0(5u32).to_flat());
|
||||
assert_eq!(acc.delayed1.into_inner(), Flat::default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_apply_at_ret() {
|
||||
struct ApplyAtRetOp;
|
||||
impl<T: Variant> ElementOp<T> for ApplyAtRetOp {
|
||||
type Output = Flat;
|
||||
fn call(&self, el: &T) -> Self::Output {
|
||||
el.to_flat()
|
||||
}
|
||||
}
|
||||
|
||||
let list = LLNode::new(V0(5u32))
|
||||
.prepend(V1(4f32))
|
||||
.prepend(V0(2u32));
|
||||
|
||||
assert_eq!(list.apply_at(0, ApplyAtRetOp), V0(2u32).to_flat());
|
||||
assert_eq!(list.apply_at(1, ApplyAtRetOp), V1(4f32).to_flat());
|
||||
assert_eq!(list.apply_at(2, ApplyAtRetOp), V0(5u32).to_flat());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get() {
|
||||
let list = LLNode::new(3f32)
|
||||
.prepend(4i32)
|
||||
.prepend(5u32);
|
||||
assert_eq!(list.get(P0), &5u32);
|
||||
assert_eq!(list.get(P1::default()), &4i32);
|
||||
assert_eq!(list.get(P2::default()), &3f32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_list_builder() {
|
||||
let type_level = List::<(i32, f32, u32)>::default();
|
||||
let value_level = LLNode::new(0u32)
|
||||
.prepend(0f32)
|
||||
.prepend(0i32);
|
||||
|
||||
assert!(type_level == value_level);
|
||||
}
|
||||
}
|
141
crates/types/src/compound/list/flat.rs
Normal file
141
crates/types/src/compound/list/flat.rs
Normal file
@@ -0,0 +1,141 @@
|
||||
use crate::compound::list::{Indexable, Meta};
|
||||
use crate::compound::peano::{P0, P1, P2, P3, Peano, PNext};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
||||
#[cfg_attr(feature = "fmt", derive(Debug))]
|
||||
#[derive(Copy, Clone, Default, PartialEq)]
|
||||
pub struct Node<H, T> {
|
||||
head: H,
|
||||
tail: T,
|
||||
}
|
||||
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
||||
#[cfg_attr(feature = "fmt", derive(Debug))]
|
||||
#[derive(Copy, Clone, Default, PartialEq)]
|
||||
pub struct Null;
|
||||
|
||||
impl<H, T> Node<H, T> {
|
||||
fn new(head: H, tail: T) -> Self {
|
||||
Self { head, tail }
|
||||
}
|
||||
|
||||
pub fn get<P: Peano>(&self) -> <Self as Indexable<P>>::Element
|
||||
where
|
||||
Self: Indexable<P>,
|
||||
<Self as Indexable<P>>::Element: Copy,
|
||||
{
|
||||
Indexable::<P>::get(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E0, T> Indexable<P0> for Node<E0, T> {
|
||||
type Element = E0;
|
||||
fn get(&self) -> Self::Element where Self::Element: Copy {
|
||||
self.head
|
||||
}
|
||||
fn get_ref(&self) -> &Self::Element {
|
||||
&self.head
|
||||
}
|
||||
fn get_mut(&mut self) -> &mut Self::Element {
|
||||
&mut self.head
|
||||
}
|
||||
fn set(&mut self, v: Self::Element) {
|
||||
self.head = v;
|
||||
}
|
||||
}
|
||||
|
||||
impl<E0, E1, T> Indexable<P1> for Node<E0, Node<E1, T>> {
|
||||
type Element = E1;
|
||||
fn get(&self) -> Self::Element where Self::Element: Copy {
|
||||
self.tail.head
|
||||
}
|
||||
fn get_ref(&self) -> &Self::Element {
|
||||
&self.tail.head
|
||||
}
|
||||
fn get_mut(&mut self) -> &mut Self::Element {
|
||||
&mut self.tail.head
|
||||
}
|
||||
fn set(&mut self, v: Self::Element) {
|
||||
self.tail.head = v;
|
||||
}
|
||||
}
|
||||
|
||||
impl<E0, E1, E2, T> Indexable<P2> for Node<E0, Node<E1, Node<E2, T>>> {
|
||||
type Element = E2;
|
||||
fn get(&self) -> Self::Element where Self::Element: Copy {
|
||||
self.tail.tail.head
|
||||
}
|
||||
fn get_ref(&self) -> &Self::Element {
|
||||
&self.tail.tail.head
|
||||
}
|
||||
fn get_mut(&mut self) -> &mut Self::Element {
|
||||
&mut self.tail.tail.head
|
||||
}
|
||||
fn set(&mut self, v: Self::Element) {
|
||||
self.tail.tail.head = v;
|
||||
}
|
||||
}
|
||||
|
||||
impl<E0, E1, E2, E3, T> Indexable<P3> for Node<E0, Node<E1, Node<E2, Node<E3, T>>>> {
|
||||
type Element = E3;
|
||||
fn get(&self) -> Self::Element where Self::Element: Copy {
|
||||
self.tail.tail.tail.head
|
||||
}
|
||||
fn get_ref(&self) -> &Self::Element {
|
||||
&self.tail.tail.tail.head
|
||||
}
|
||||
fn get_mut(&mut self) -> &mut Self::Element {
|
||||
&mut self.tail.tail.tail.head
|
||||
}
|
||||
fn set(&mut self, v: Self::Element) {
|
||||
self.tail.tail.tail.head = v;
|
||||
}
|
||||
}
|
||||
|
||||
impl<E0> IntoList for (E0,) {
|
||||
type List = Node<E0, Null>;
|
||||
fn into_list(self) -> Self::List {
|
||||
Node::new(self.0, Null)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E0, E1> IntoList for (E0, E1) {
|
||||
type List = Node<E0, <(E1,) as IntoList>::List>;
|
||||
fn into_list(self) -> Self::List {
|
||||
Node::new(self.0, (self.1,).into_list())
|
||||
}
|
||||
}
|
||||
|
||||
impl<E0, E1, E2> IntoList for (E0, E1, E2) {
|
||||
type List = Node<E0, <(E1, E2) as IntoList>::List>;
|
||||
fn into_list(self) -> Self::List {
|
||||
Node::new(self.0, (self.1, self.2).into_list())
|
||||
}
|
||||
}
|
||||
|
||||
impl<E0, E1, E2, E3> IntoList for (E0, E1, E2, E3) {
|
||||
type List = Node<E0, <(E1, E2, E3) as IntoList>::List>;
|
||||
fn into_list(self) -> Self::List {
|
||||
Node::new(self.0, (self.1, self.2, self.3).into_list())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IntoList {
|
||||
type List;
|
||||
fn into_list(self) -> Self::List;
|
||||
}
|
||||
|
||||
pub type List<Args> = <Args as IntoList>::List;
|
||||
pub type List1<E0> = Node<E0, Null>;
|
||||
pub type List2<E0, E1> = Node<E0, List1<E1>>;
|
||||
pub type List3<E0, E1, E2> = Node<E0, List2<E1, E2>>;
|
||||
pub type List4<E0, E1, E2, E3> = Node<E0, List3<E1, E2, E3>>;
|
||||
|
||||
impl<H> Meta for Node<H, Null> {
|
||||
type Length = P1;
|
||||
}
|
||||
impl<H, T: Meta> Meta for Node<H, T> {
|
||||
type Length = PNext<T::Length>;
|
||||
}
|
130
crates/types/src/compound/list/linked.rs
Normal file
130
crates/types/src/compound/list/linked.rs
Normal file
@@ -0,0 +1,130 @@
|
||||
use crate::compound::peano::{P0, Peano, PeanoNonZero};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
||||
#[cfg_attr(feature = "fmt", derive(Debug))]
|
||||
#[derive(Copy, Clone, Default, PartialEq)]
|
||||
pub struct Middle<H, T> {
|
||||
head: H,
|
||||
tail: T,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
||||
#[cfg_attr(feature = "fmt", derive(Debug))]
|
||||
#[derive(Copy, Clone, Default, PartialEq)]
|
||||
pub struct Terminal<H> {
|
||||
head: H,
|
||||
// XXX: needed to handle ZSTs in spirv, else we can't hand out a reference to &self
|
||||
_pad: u32,
|
||||
}
|
||||
|
||||
impl<H, T> Middle<H, T> {
|
||||
fn new(head: H, tail: T) -> Self {
|
||||
Middle { head, tail }
|
||||
}
|
||||
}
|
||||
|
||||
impl<H> Terminal<H> {
|
||||
fn new(head: H) -> Self {
|
||||
Terminal { head, _pad: Default::default() }
|
||||
}
|
||||
}
|
||||
|
||||
// Self is a Superlist of L
|
||||
pub trait Superlist<Distance: Peano> {
|
||||
type Of: ListOps;
|
||||
fn as_sublist(&self) -> &Self::Of;
|
||||
}
|
||||
|
||||
impl<L: ListOps> Superlist<P0> for L {
|
||||
type Of = L;
|
||||
fn as_sublist(&self) -> &Self::Of {
|
||||
self
|
||||
}
|
||||
}
|
||||
// if our tail T0 is a Superlist<P-1> of T1,
|
||||
// then we are a Superlist<P> of T1.
|
||||
impl<H0, T0: ListOps, T1: ListOps, P: PeanoNonZero> Superlist<P> for Middle<H0, T0>
|
||||
where
|
||||
T0: Superlist<P::Prev, Of=T1>
|
||||
{
|
||||
type Of = T1;
|
||||
fn as_sublist(&self) -> &T1 {
|
||||
self.tail.as_sublist()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ListOps {
|
||||
type Element;
|
||||
fn read(&self) -> Self::Element where Self::Element: Copy;
|
||||
|
||||
fn index<P: Peano>(&self) -> &<Self as Superlist<P>>::Of
|
||||
where Self: Superlist<P>
|
||||
{
|
||||
self.as_sublist()
|
||||
}
|
||||
fn get<P: Peano>(&self) -> <<Self as Superlist<P>>::Of as ListOps>::Element
|
||||
where
|
||||
Self: Superlist<P>,
|
||||
<<Self as Superlist<P>>::Of as ListOps>::Element: Copy
|
||||
|
||||
{
|
||||
self.index::<P>().read()
|
||||
}
|
||||
}
|
||||
|
||||
impl<H> ListOps for Terminal<H> {
|
||||
type Element = H;
|
||||
fn read(&self) -> Self::Element where Self::Element: Copy {
|
||||
self.head
|
||||
}
|
||||
}
|
||||
|
||||
impl<H, T> ListOps for Middle<H, T> {
|
||||
type Element = H;
|
||||
fn read(&self) -> Self::Element where Self::Element: Copy {
|
||||
self.head
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IntoList {
|
||||
type List;
|
||||
fn into_list(self) -> Self::List;
|
||||
}
|
||||
|
||||
impl<E0> IntoList for (E0,) {
|
||||
type List = Terminal<E0>;
|
||||
fn into_list(self) -> Self::List {
|
||||
Terminal::new(self.0)
|
||||
}
|
||||
}
|
||||
impl<E0, E1> IntoList for (E0, E1) {
|
||||
type List = Middle<E0, <(E1,) as IntoList>::List>;
|
||||
fn into_list(self) -> Self::List {
|
||||
Middle::new(self.0, (self.1,).into_list())
|
||||
}
|
||||
}
|
||||
impl<E0, E1, E2> IntoList for (E0, E1, E2) {
|
||||
type List = Middle<E0, <(E1, E2) as IntoList>::List>;
|
||||
fn into_list(self) -> Self::List {
|
||||
Middle::new(self.0, (self.1, self.2).into_list())
|
||||
}
|
||||
}
|
||||
|
||||
pub type List<Args> = <Args as IntoList>::List;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::compound::peano::{P0, P1, P2};
|
||||
#[test]
|
||||
fn list_index() {
|
||||
let l = (1u32, 3f32, 4u32).into_list();
|
||||
assert_eq!(l.read(), 1u32);
|
||||
assert_eq!(l.index::<P0>().read(), 1u32);
|
||||
assert_eq!(l.index::<P1>().read(), 3f32);
|
||||
assert_eq!(l.index::<P2>().read(), 4u32);
|
||||
}
|
||||
}
|
22
crates/types/src/compound/list/mod.rs
Normal file
22
crates/types/src/compound/list/mod.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
use crate::compound::peano::{Peano, PeanoNonZero};
|
||||
|
||||
mod flat;
|
||||
mod linked;
|
||||
mod tuple_consumer;
|
||||
// pub use tuple_consumer::*;
|
||||
// pub use linked::*;
|
||||
pub use flat::*;
|
||||
|
||||
pub trait Indexable<P: Peano> {
|
||||
type Element;
|
||||
fn get(&self) -> Self::Element where Self::Element: Copy;
|
||||
fn get_ref(&self) -> &Self::Element;
|
||||
fn get_mut(&mut self) -> &mut Self::Element;
|
||||
fn set(&mut self, v: Self::Element);
|
||||
}
|
||||
|
||||
pub type ElementAt<P, L> = <L as Indexable<P>>::Element;
|
||||
|
||||
pub trait Meta {
|
||||
type Length: PeanoNonZero;
|
||||
}
|
161
crates/types/src/compound/list/tuple_consumer.rs
Normal file
161
crates/types/src/compound/list/tuple_consumer.rs
Normal file
@@ -0,0 +1,161 @@
|
||||
use crate::compound::peano::{P0, PNext};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
pub trait TuplePrims: Sized {
|
||||
type Head; // single element
|
||||
type Tail; // variably-sized tuple
|
||||
fn into_head(self) -> Self::Head;
|
||||
fn into_tail(self) -> Self::Tail;
|
||||
}
|
||||
impl<E0> TuplePrims for (E0,) {
|
||||
type Head = E0;
|
||||
type Tail = ();
|
||||
fn into_head(self) -> Self::Head {
|
||||
self.0
|
||||
}
|
||||
fn into_tail(self) -> Self::Tail {
|
||||
()
|
||||
}
|
||||
}
|
||||
impl<E0, E1> TuplePrims for (E0, E1) {
|
||||
type Head = E0;
|
||||
type Tail = (E1,);
|
||||
fn into_head(self) -> Self::Head {
|
||||
self.0
|
||||
}
|
||||
fn into_tail(self) -> Self::Tail {
|
||||
(self.1,)
|
||||
}
|
||||
}
|
||||
impl<E0, E1, E2> TuplePrims for (E0, E1, E2) {
|
||||
type Head = E0;
|
||||
type Tail = (E1, E2);
|
||||
fn into_head(self) -> Self::Head {
|
||||
self.0
|
||||
}
|
||||
fn into_tail(self) -> Self::Tail {
|
||||
(self.1, self.2)
|
||||
}
|
||||
}
|
||||
impl<E0, E1, E2, E3> TuplePrims for (E0, E1, E2, E3) {
|
||||
type Head = E0;
|
||||
type Tail = (E1, E2, E3);
|
||||
fn into_head(self) -> Self::Head {
|
||||
self.0
|
||||
}
|
||||
fn into_tail(self) -> Self::Tail {
|
||||
(self.1, self.2, self.3)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// note that this construction allows a zero-length list (Null),
|
||||
// which is sort of interesting.
|
||||
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
||||
#[cfg_attr(feature = "fmt", derive(Debug))]
|
||||
#[derive(Copy, Clone, Default, PartialEq)]
|
||||
pub struct List<Args>(Args);
|
||||
|
||||
impl<Args> List<Args> {
|
||||
pub fn new(args: Args) -> Self {
|
||||
Self(args)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ListPrims: Sized {
|
||||
type Head; // single element
|
||||
type Tail; // variably-sized List
|
||||
fn into_head(self) -> Self::Head;
|
||||
fn into_tail(self) -> Self::Tail;
|
||||
}
|
||||
|
||||
impl<Args: TuplePrims> ListPrims for List<Args> {
|
||||
type Head = Args::Head;
|
||||
type Tail = List<Args::Tail>;
|
||||
fn into_head(self) -> Self::Head {
|
||||
self.0.into_head()
|
||||
}
|
||||
fn into_tail(self) -> Self::Tail {
|
||||
List(self.0.into_tail())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Consumable<P> {
|
||||
type Result: ListPrims;
|
||||
fn consume(self) -> Self::Result;
|
||||
}
|
||||
|
||||
impl<Args: TuplePrims> Consumable<P0> for List<Args> {
|
||||
type Result = Self;
|
||||
fn consume(self) -> Self::Result {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<Args, P> Consumable<PNext<P>> for List<Args>
|
||||
where
|
||||
Self: ListPrims,
|
||||
<Self as ListPrims>::Tail: Consumable<P>,
|
||||
{
|
||||
type Result = <<Self as ListPrims>::Tail as Consumable<P>>::Result;
|
||||
fn consume(self) -> Self::Result {
|
||||
self.into_tail().consume()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Args: TuplePrims> List<Args> {
|
||||
pub fn consume<P>(self) -> <Self as Consumable<P>>::Result
|
||||
where Self: Consumable<P>
|
||||
{
|
||||
Consumable::<P>::consume(self)
|
||||
}
|
||||
pub fn get<P>(self) -> <<Self as Consumable<P>>::Result as ListPrims>::Head
|
||||
where Self: Consumable<P>
|
||||
{
|
||||
self.consume().into_head()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::compound::peano::{P1, P2};
|
||||
|
||||
#[test]
|
||||
fn get() {
|
||||
let list = List((5u32, 4i32, 3f32));
|
||||
|
||||
assert_eq!(list.get::<P0>(), 5u32);
|
||||
assert_eq!(list.get::<P1>(), 4i32);
|
||||
assert_eq!(list.get::<P2>(), 3f32);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn get() {
|
||||
// let list = (5u32, 4i32, 3f32).into_list();
|
||||
// assert_eq!(list.get::<P0>(), &5u32);
|
||||
// assert_eq!(list.get::<P1>(), &4i32);
|
||||
// assert_eq!(list.get::<P2>(), &3f32);
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn set() {
|
||||
// let mut list = List::<(u32, i32, f32)>::default();
|
||||
|
||||
// list.set::<P0>(5u32);
|
||||
|
||||
// assert_eq!(list.get::<P0>(), &5u32);
|
||||
// assert_eq!(list.get::<P1>(), &0i32);
|
||||
// assert_eq!(list.get::<P2>(), &0f32);
|
||||
|
||||
// list.set::<P2>(3f32);
|
||||
// list.set::<P1>(4i32);
|
||||
|
||||
// assert_eq!(list.get::<P0>(), &5u32);
|
||||
// assert_eq!(list.get::<P1>(), &4i32);
|
||||
// assert_eq!(list.get::<P2>(), &3f32);
|
||||
// }
|
||||
}
|
@@ -1,2 +1,3 @@
|
||||
pub mod enumerated;
|
||||
pub mod list;
|
||||
pub mod peano;
|
||||
|
@@ -1,7 +1,8 @@
|
||||
#[derive(Default)]
|
||||
#[derive(Copy, Clone, Default, PartialEq)]
|
||||
pub struct PNext<P>(P);
|
||||
#[derive(Default)]
|
||||
#[derive(Copy, Clone, Default, PartialEq)]
|
||||
pub struct P0;
|
||||
|
||||
pub type P1 = PNext<P0>;
|
||||
pub type P2 = PNext<P1>;
|
||||
pub type P3 = PNext<P2>;
|
||||
@@ -10,8 +11,13 @@ pub type P5 = PNext<P4>;
|
||||
pub type P6 = PNext<P5>;
|
||||
pub type P7 = PNext<P6>;
|
||||
|
||||
pub trait Peano: Default {
|
||||
pub trait Peano: Copy + Clone + Default + PartialEq {
|
||||
type Next: PeanoNonZero;
|
||||
type PrevOrZero: Peano;
|
||||
/// always set to ()
|
||||
/// this exists to allow Peano numbers to be used as struct parameters without PhantomData
|
||||
type Unit: Copy + Clone + Default + PartialEq;
|
||||
const VALUE: u32;
|
||||
}
|
||||
|
||||
pub trait PeanoNonZero: Peano {
|
||||
@@ -20,10 +26,21 @@ pub trait PeanoNonZero: Peano {
|
||||
|
||||
impl Peano for P0 {
|
||||
type Next = P1;
|
||||
type PrevOrZero = P0;
|
||||
type Unit = ();
|
||||
const VALUE: u32 = 0;
|
||||
}
|
||||
impl<P: Peano> Peano for PNext<P> {
|
||||
type Next = PNext<PNext<P>>;
|
||||
type PrevOrZero = P;
|
||||
type Unit = ();
|
||||
const VALUE: u32 = 1 + P::VALUE;
|
||||
}
|
||||
impl<P: Peano> PeanoNonZero for PNext<P> {
|
||||
type Prev = P;
|
||||
}
|
||||
|
||||
// A: LessThan<B> is satisfied only if A is strictly less than B.
|
||||
pub trait LessThan<P: Peano> { }
|
||||
impl<P: Peano> LessThan<PNext<P>> for P { }
|
||||
|
||||
|
236
crates/types/src/mat/compound.rs
Normal file
236
crates/types/src/mat/compound.rs
Normal file
@@ -0,0 +1,236 @@
|
||||
use crate::compound::enumerated::{DiscriminantCodable, Enum, EnumRequirements, VariantHandler};
|
||||
use crate::compound::list::{Indexable, List2, List3};
|
||||
use crate::compound::peano::{Peano, P0, P1, P2, P3};
|
||||
use crate::real::Real;
|
||||
use crate::vec::Vec3;
|
||||
use crate::mat::{
|
||||
AnisomorphicConductor,
|
||||
Ferroxcube3R1MH,
|
||||
IsomorphicConductor,
|
||||
Material,
|
||||
MBPgram,
|
||||
MHPgram,
|
||||
Vacuum,
|
||||
};
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
/// a material which can take on 1 of N types.
|
||||
/// it's assumed that the first type in the material list is capable of storing the discriminant
|
||||
/// (i.e. that it implements DiscriminantCodable).
|
||||
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
||||
#[cfg_attr(feature = "fmt", derive(Debug))]
|
||||
#[derive(Copy, Clone, Default, PartialEq)]
|
||||
pub struct DiscrMat<Mats>(Enum<(), Mats>);
|
||||
pub type DiscrMat2<M0, M1> = DiscrMat<List2<M0, M1>>;
|
||||
pub type DiscrMat3<M0, M1, M2> = DiscrMat<List3<M0, M1, M2>>;
|
||||
|
||||
impl<Mats> DiscrMat<Mats> {
|
||||
fn new_from_fields(mats: Mats) -> Self {
|
||||
Self(Enum::from_components((), mats))
|
||||
}
|
||||
}
|
||||
|
||||
impl<Mats: Default> DiscrMat<Mats> {
|
||||
fn new<P: Peano>(m: Mats::Element) -> Self
|
||||
where
|
||||
Mats: Indexable<P>,
|
||||
Enum<(), Mats>: EnumRequirements,
|
||||
{
|
||||
let mut me = Self::default();
|
||||
me.0.set::<P>(m);
|
||||
me
|
||||
}
|
||||
}
|
||||
|
||||
/// invokes Material::conductivity on any Material
|
||||
struct ConductivityDispatcher;
|
||||
impl<P: Peano, R: Real, T: Material<R>> VariantHandler<P, T, Vec3<R>> for ConductivityDispatcher {
|
||||
fn call(self, v: T) -> Vec3<R> {
|
||||
v.conductivity()
|
||||
}
|
||||
}
|
||||
|
||||
/// invokes Material::move_b_vec on any Material
|
||||
struct MoveBVecDispatcher<R> {
|
||||
m: Vec3<R>,
|
||||
target_b: Vec3<R>,
|
||||
}
|
||||
impl<R: Real, P: Peano, T: Material<R>> VariantHandler<P, T, Vec3<R>> for MoveBVecDispatcher<R> {
|
||||
fn call(self, v: T) -> Vec3<R> {
|
||||
v.move_b_vec(self.m, self.target_b)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Real, M0, M1> Material<R> for DiscrMat2<M0, M1>
|
||||
where
|
||||
M0: DiscriminantCodable<P2> + Material<R> + Copy,
|
||||
M1: Material<R> + Copy,
|
||||
{
|
||||
fn conductivity(&self) -> Vec3<R> {
|
||||
self.0.dispatch(ConductivityDispatcher)
|
||||
}
|
||||
fn move_b_vec(&self, m: Vec3<R>, target_b: Vec3<R>) -> Vec3<R> {
|
||||
self.0.dispatch(MoveBVecDispatcher { m, target_b })
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Real, M0, M1, M2> Material<R> for DiscrMat3<M0, M1, M2>
|
||||
where
|
||||
M0: DiscriminantCodable<P3> + Material<R> + Copy,
|
||||
M1: Material<R> + Copy,
|
||||
M2: Material<R> + Copy,
|
||||
{
|
||||
fn conductivity(&self) -> Vec3<R> {
|
||||
self.0.dispatch(ConductivityDispatcher)
|
||||
}
|
||||
fn move_b_vec(&self, m: Vec3<R>, target_b: Vec3<R>) -> Vec3<R> {
|
||||
self.0.dispatch(MoveBVecDispatcher { m, target_b })
|
||||
}
|
||||
}
|
||||
|
||||
/// represents a Material which is either an isomorphic conductor, or some other Material `M1`
|
||||
pub type IsoConductorOr<R, M1> = DiscrMat2<IsomorphicConductor<R>, M1>;
|
||||
|
||||
// XXX: can't do this for generic M, because that creates duplicate `From` impls for the
|
||||
// IsomorphicConductor itself
|
||||
impl<R: Real> From<Ferroxcube3R1MH> for IsoConductorOr<R, Ferroxcube3R1MH> {
|
||||
fn from(mat: Ferroxcube3R1MH) -> Self {
|
||||
IsoConductorOr::new::<P1>(mat)
|
||||
}
|
||||
}
|
||||
impl<R: Real> From<IsomorphicConductor<R>> for IsoConductorOr<R, Ferroxcube3R1MH> {
|
||||
fn from(mat: IsomorphicConductor<R>) -> Self {
|
||||
IsoConductorOr::new::<P0>(mat)
|
||||
}
|
||||
}
|
||||
|
||||
/// muxes operations to either the conductor or the magnetic material
|
||||
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
||||
#[cfg_attr(feature = "fmt", derive(Debug))]
|
||||
#[derive(Copy, Clone, Default, PartialEq)]
|
||||
pub struct DualMaterial<C, M> {
|
||||
conductor: C,
|
||||
magnetic: M,
|
||||
}
|
||||
|
||||
impl<C, M> DualMaterial<C, M> {
|
||||
pub fn new(conductor: C, magnetic: M) -> Self {
|
||||
Self { conductor, magnetic }
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Real, C: Material<R>, M: Material<R>> Material<R> for DualMaterial<C, M> {
|
||||
fn conductivity(&self) -> Vec3<R> {
|
||||
self.conductor.conductivity()
|
||||
}
|
||||
fn move_b_vec(&self, m: Vec3<R>, target_b: Vec3<R>) -> Vec3<R> {
|
||||
self.magnetic.move_b_vec(m, target_b)
|
||||
}
|
||||
}
|
||||
|
||||
/// Material which can encode any of the well-known magnetic materials (or Vacuum)
|
||||
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
||||
#[cfg_attr(feature = "fmt", derive(Debug))]
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub struct GenericMagnetic<R>(DiscrMat3<MBPgram<R>, MHPgram<R>, Vacuum>);
|
||||
|
||||
impl<R: Real> Default for GenericMagnetic<R> {
|
||||
fn default() -> Self {
|
||||
// N.B.: the default is not the first variant.
|
||||
// we order the variants specifically so that the first one can store the descriminant, but
|
||||
// we NEED Vacuum to be the default.
|
||||
Vacuum.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Real> Material<R> for GenericMagnetic<R> {
|
||||
fn conductivity(&self) -> Vec3<R> {
|
||||
self.0.conductivity()
|
||||
}
|
||||
fn move_b_vec(&self, m: Vec3<R>, target_b: Vec3<R>) -> Vec3<R> {
|
||||
self.0.move_b_vec(m, target_b)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Real> From<MBPgram<R>> for GenericMagnetic<R> {
|
||||
fn from(mat: MBPgram<R>) -> Self {
|
||||
GenericMagnetic(DiscrMat3::new::<P0>(mat))
|
||||
}
|
||||
}
|
||||
impl<R: Real> From<MHPgram<R>> for GenericMagnetic<R> {
|
||||
fn from(mat: MHPgram<R>) -> Self {
|
||||
GenericMagnetic(DiscrMat3::new::<P1>(mat))
|
||||
}
|
||||
}
|
||||
impl<R: Real> From<Vacuum> for GenericMagnetic<R> {
|
||||
fn from(mat: Vacuum) -> Self {
|
||||
GenericMagnetic(DiscrMat3::new::<P2>(mat))
|
||||
}
|
||||
}
|
||||
|
||||
/// "Fully Generic" in that one can set both the conductivity,
|
||||
/// and set any of the well-known magnetic materials, simultaneously.
|
||||
pub type FullyGenericMaterial<R> = DualMaterial<
|
||||
AnisomorphicConductor<R>,
|
||||
GenericMagnetic<R>,
|
||||
>;
|
||||
|
||||
impl<R: Real> From<AnisomorphicConductor<R>> for FullyGenericMaterial<R> {
|
||||
fn from(mat: AnisomorphicConductor<R>) -> Self {
|
||||
Self::new(mat, Default::default())
|
||||
}
|
||||
}
|
||||
impl<R: Real> From<MBPgram<R>> for FullyGenericMaterial<R> {
|
||||
fn from(mat: MBPgram<R>) -> Self {
|
||||
Self::new(Default::default(), mat.into())
|
||||
}
|
||||
}
|
||||
impl<R: Real> From<MHPgram<R>> for FullyGenericMaterial<R> {
|
||||
fn from(mat: MHPgram<R>) -> Self {
|
||||
Self::new(Default::default(), mat.into())
|
||||
}
|
||||
}
|
||||
impl<R: Real> From<IsomorphicConductor<R>> for FullyGenericMaterial<R> {
|
||||
fn from(mat: IsomorphicConductor<R>) -> Self {
|
||||
let mat: AnisomorphicConductor<R> = mat.into();
|
||||
mat.into()
|
||||
}
|
||||
}
|
||||
impl<R: Real> From<Ferroxcube3R1MH> for FullyGenericMaterial<R> {
|
||||
fn from(mat: Ferroxcube3R1MH) -> Self {
|
||||
let mat: MHPgram<R> = mat.into();
|
||||
mat.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::mat::AnisomorphicConductor;
|
||||
use crate::real::R32;
|
||||
|
||||
#[test]
|
||||
fn iso_conductor_or_3r1() {
|
||||
let c: IsoConductorOr<f32, Ferroxcube3R1MH> = IsomorphicConductor::new(22.0f32).into();
|
||||
assert!(c.conductivity() == Vec3::uniform(22.0f32));
|
||||
|
||||
let c: IsoConductorOr<f32, Ferroxcube3R1MH> = Ferroxcube3R1MH::new().into();
|
||||
assert!(c.conductivity() == Vec3::zero());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iso_conductor_or_aniso() {
|
||||
type I = IsoConductorOr<R32, AnisomorphicConductor<R32>>;
|
||||
|
||||
let c = I::new::<P0>(IsomorphicConductor::new(22f32.cast()));
|
||||
assert!(c.conductivity() == Vec3::uniform(22.0).cast());
|
||||
|
||||
let c = I::new::<P1>(AnisomorphicConductor::new(
|
||||
Vec3::new(2.0, 3.0, 4.0).cast()
|
||||
));
|
||||
|
||||
assert!(c.conductivity() == Vec3::new(2.0, 3.0, 4.0).cast());
|
||||
}
|
||||
}
|
@@ -1,4 +1,6 @@
|
||||
use crate::mat::{DiscriminantCodable, Material};
|
||||
use crate::compound::enumerated::{Discr, DiscriminantCodable};
|
||||
use crate::compound::peano::Peano;
|
||||
use crate::mat::Material;
|
||||
use crate::real::Real;
|
||||
use crate::vec::Vec3;
|
||||
|
||||
@@ -48,16 +50,56 @@ impl<R: Real> Material<R> for IsomorphicConductor<R> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Real> DiscriminantCodable for IsomorphicConductor<R> {
|
||||
fn discr(&self) -> u32 {
|
||||
let cond = self.iso_conductivity();
|
||||
if cond < R::zero() {
|
||||
impl<R: Real, P: Peano> DiscriminantCodable<P> for AnisomorphicConductor<R> {
|
||||
fn decode_discr(&self) -> Discr<P> {
|
||||
let cond = self.conductivity().x();
|
||||
let d = if cond < R::zero() {
|
||||
(-cond.to_f32()) as u32
|
||||
} else {
|
||||
0
|
||||
}
|
||||
};
|
||||
Discr::new(d)
|
||||
}
|
||||
fn make_discr(d: u32) -> Self {
|
||||
Self::new(R::from_primitive(-(d as i32)))
|
||||
fn encode_discr(d: Discr<P>) -> Self {
|
||||
Self::new(Vec3::new_x(
|
||||
R::from_primitive(-(d.value() as i32))
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Real, P: Peano> DiscriminantCodable<P> for IsomorphicConductor<R> {
|
||||
fn decode_discr(&self) -> Discr<P> {
|
||||
let cond = self.iso_conductivity();
|
||||
let d = if cond < R::zero() {
|
||||
(-cond.to_f32()) as u32
|
||||
} else {
|
||||
0
|
||||
};
|
||||
Discr::new(d)
|
||||
}
|
||||
fn encode_discr(d: Discr<P>) -> Self {
|
||||
Self::new(R::from_primitive(-(d.value() as i32)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::compound::peano::P6;
|
||||
#[test]
|
||||
fn iso_conductor_discr() {
|
||||
type T = IsomorphicConductor<f32>;
|
||||
let c = <T as DiscriminantCodable<P6>>::encode_discr(Discr::new(5));
|
||||
assert_eq!(DiscriminantCodable::<P6>::decode_discr(&c).value(), 5);
|
||||
|
||||
let c = <T as DiscriminantCodable<P6>>::encode_discr(Discr::new(0));
|
||||
assert_eq!(DiscriminantCodable::<P6>::decode_discr(&c).value(), 0);
|
||||
|
||||
let c = T::new(5.0);
|
||||
assert_eq!(DiscriminantCodable::<P6>::decode_discr(&c).value(), 0);
|
||||
|
||||
let c = T::new(0.0);
|
||||
assert_eq!(DiscriminantCodable::<P6>::decode_discr(&c).value(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,3 +1,5 @@
|
||||
use crate::compound::enumerated::{Discr, DiscriminantCodable};
|
||||
use crate::compound::peano::Peano;
|
||||
use crate::mat::Material;
|
||||
use crate::real::Real;
|
||||
use crate::vec::Vec3;
|
||||
@@ -67,6 +69,24 @@ impl<R: Real> Material<R> for MBPgram<R> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Real, P: Peano> DiscriminantCodable<P> for MBPgram<R> {
|
||||
fn decode_discr(&self) -> Discr<P> {
|
||||
let max_m = self.max_m;
|
||||
let d = if max_m < R::zero() {
|
||||
(-max_m.to_f32()) as u32
|
||||
} else {
|
||||
0
|
||||
};
|
||||
Discr::new(d)
|
||||
}
|
||||
fn encode_discr(d: Discr<P>) -> Self {
|
||||
Self {
|
||||
max_m: R::from_primitive(-(d.value() as i32)),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
@@ -1,17 +1,19 @@
|
||||
use crate::real::Real;
|
||||
use crate::vec::Vec3;
|
||||
|
||||
mod compound;
|
||||
mod conductor;
|
||||
mod mb_pgram;
|
||||
mod mh_pgram;
|
||||
pub use compound::{FullyGenericMaterial, IsoConductorOr};
|
||||
pub use conductor::{AnisomorphicConductor, IsomorphicConductor};
|
||||
pub use mb_pgram::MBPgram;
|
||||
pub use mh_pgram::{Ferroxcube3R1MH, MHPgram};
|
||||
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
|
||||
pub trait Material<R: Real>: Sized {
|
||||
fn conductivity(&self) -> Vec3<R> {
|
||||
Default::default()
|
||||
@@ -24,143 +26,10 @@ pub trait Material<R: Real>: Sized {
|
||||
}
|
||||
}
|
||||
|
||||
/// any type which may hold a discriminant internally.
|
||||
pub trait DiscriminantCodable {
|
||||
fn discr(&self) -> u32;
|
||||
fn make_discr(d: u32) -> Self;
|
||||
}
|
||||
|
||||
pub struct D0<T>(pub T);
|
||||
pub struct D1<T>(pub T);
|
||||
|
||||
/// Default, non-interesting Material.
|
||||
/// has 0 conductivity (electrical and magnetic)
|
||||
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
||||
#[cfg_attr(feature = "fmt", derive(Debug))]
|
||||
#[derive(Copy, Clone, Default, PartialEq)]
|
||||
pub struct DiscrMat2<M0, M1> {
|
||||
m0: M0,
|
||||
m1: M1,
|
||||
}
|
||||
|
||||
impl<M0, M1> DiscrMat2<M0, M1> {
|
||||
fn new_from_fields(m0: M0, m1: M1) -> Self {
|
||||
Self { m0, m1 }
|
||||
}
|
||||
}
|
||||
|
||||
impl<M0: DiscriminantCodable, M1: Default> DiscrMat2<M0, M1> {
|
||||
pub fn new0(m: M0) -> Self {
|
||||
D0(m).into()
|
||||
}
|
||||
pub fn new1(m: M1) -> Self {
|
||||
D1(m).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<M0: DiscriminantCodable, M1: Default> From<D0<M0>> for DiscrMat2<M0, M1> {
|
||||
fn from(m0: D0<M0>) -> Self {
|
||||
let m0 = m0.0;
|
||||
assert!(m0.discr() == 0, "unexpected non-zero discriminant");
|
||||
Self::new_from_fields(m0, Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<M0: DiscriminantCodable, M1> From<D1<M1>> for DiscrMat2<M0, M1> {
|
||||
fn from(m1: D1<M1>) -> Self {
|
||||
let m1 = m1.0;
|
||||
Self::new_from_fields(M0::make_discr(1), m1)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Real, M0: DiscriminantCodable + Material<R>, M1: Material<R> + Copy> Material<R> for DiscrMat2<M0, M1> {
|
||||
fn conductivity(&self) -> Vec3<R> {
|
||||
match self.m0.discr() {
|
||||
0 => self.m0.conductivity(),
|
||||
1 => {
|
||||
let mat = self.m1; //< XXX hack for ZST
|
||||
mat.conductivity()
|
||||
}
|
||||
_ => panic!()
|
||||
// _ => unreachable!(),
|
||||
}
|
||||
}
|
||||
fn move_b_vec(&self, m: Vec3<R>, target_b: Vec3<R>) -> Vec3<R> {
|
||||
match self.m0.discr() {
|
||||
0 => self.m0.move_b_vec(m, target_b),
|
||||
1 => {
|
||||
let mat = self.m1; //< XXX hack for ZST
|
||||
mat.move_b_vec(m, target_b)
|
||||
}
|
||||
_ => panic!()
|
||||
// _ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type IsoConductorOr<R, M1> = DiscrMat2<IsomorphicConductor<R>, M1>;
|
||||
|
||||
impl<R: Real, M1: Default> IsoConductorOr<R, M1> {
|
||||
pub fn new_conductor(conductivity: R) -> Self {
|
||||
Self::new0(IsomorphicConductor::new(conductivity))
|
||||
}
|
||||
pub fn new_other(other: M1) -> Self {
|
||||
Self::new1(other)
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: can't do this for generic M, because that creates duplicate `From` impls for the
|
||||
// IsomorphicConductor itself
|
||||
impl<R: Real> From<Ferroxcube3R1MH> for IsoConductorOr<R, Ferroxcube3R1MH> {
|
||||
fn from(mat: Ferroxcube3R1MH) -> Self {
|
||||
IsoConductorOr::new1(mat)
|
||||
}
|
||||
}
|
||||
impl<R: Real> From<IsomorphicConductor<R>> for IsoConductorOr<R, Ferroxcube3R1MH> {
|
||||
fn from(mat: IsomorphicConductor<R>) -> Self {
|
||||
IsoConductorOr::new0(mat)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::real::R32;
|
||||
|
||||
#[test]
|
||||
fn iso_conductor_discr() {
|
||||
type T = IsomorphicConductor<f32>;
|
||||
let c = T::make_discr(5);
|
||||
assert_eq!(c.discr(), 5);
|
||||
|
||||
let c = T::make_discr(0);
|
||||
assert_eq!(c.discr(), 0);
|
||||
|
||||
let c = T::new(5.0);
|
||||
assert_eq!(c.discr(), 0);
|
||||
|
||||
let c = T::new(0.0);
|
||||
assert_eq!(c.discr(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iso_conductor_or_3r1() {
|
||||
let c: IsoConductorOr<f32, Ferroxcube3R1MH> = IsomorphicConductor::new(22.0f32).into();
|
||||
assert!(c.conductivity() == Vec3::uniform(22.0f32));
|
||||
|
||||
let c: IsoConductorOr<f32, Ferroxcube3R1MH> = Ferroxcube3R1MH::new().into();
|
||||
assert!(c.conductivity() == Vec3::zero());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iso_conductor_or_aniso() {
|
||||
type I = IsoConductorOr<R32, AnisomorphicConductor<R32>>;
|
||||
|
||||
let c = I::new0(IsomorphicConductor::new(22f32.cast()));
|
||||
assert!(c.conductivity() == Vec3::uniform(22.0).cast());
|
||||
|
||||
let c = I::new1(AnisomorphicConductor::new(
|
||||
Vec3::new(2.0, 3.0, 4.0).cast()
|
||||
));
|
||||
|
||||
assert!(c.conductivity() == Vec3::new(2.0, 3.0, 4.0).cast());
|
||||
}
|
||||
}
|
||||
pub struct Vacuum;
|
||||
impl<R: Real> Material<R> for Vacuum {}
|
||||
|
Reference in New Issue
Block a user