43 Commits

Author SHA1 Message Date
847b95f036 replace the FullyGenericMaterial in spirv with an analog type from coremem_types
this represents the last spirv-specific material.
next steps are to start removing the materials from `coremem` itself
(i.e. re-exporting more from `coremem_types::mat`).
2022-07-22 02:56:00 -07:00
4cbcc46d50 list: flat: remove unused import 2022-07-22 01:54:18 -07:00
727b7b43a3 types: mat: move DiscrMat into its own file 2022-07-22 01:53:42 -07:00
50ae6d4c34 types: mat: hack in a way to implement a 3-variant material 2022-07-22 01:46:07 -07:00
a8b6000104 types: list: lift the generic traits up to the top-level module
this lets me more easily swap in/out different list implementations when
experimenting.
2022-07-22 01:19:43 -07:00
fffe917c5c mat: remove some unused stuff related to DiscrMat2 2022-07-22 01:02:54 -07:00
97ac46fd8a mat: DiscrMat2 uses our Enum type internally 2022-07-22 00:44:57 -07:00
cdcc1fbbdd types: list: remove unused imports 2022-07-22 00:08:53 -07:00
72a66dbff4 types: enum: buff up tests 2022-07-21 23:50:57 -07:00
27c1523b0c types: Enum: make the DiscriminantCodable type a bit more usable
previously we couldn't *create* a discriminant, only edit it.
doing things this way is useful for the material code.
2022-07-21 23:02:33 -07:00
491f863aea types: enumerated: fix compile errors 2022-07-21 22:51:26 -07:00
fcc735765c types: Enum: simplify the internally_discriminanted constructor
we don't actually need to enforce the discriminant codability in the
constructor.
if the conditions aren't met, the user just won't be able to operate on
the enum.
2022-07-21 22:21:35 -07:00
55e58f630c coremem_types: Enum: fix a typo in the DiscriminantCodable docs 2022-07-21 22:15:52 -07:00
f2bb16eb5b coremem_types: Enum: add constructors 2022-07-21 22:11:48 -07:00
19b1df4919 coremem_types: Enum: add tests for the internal discriminant case 2022-07-21 21:58:41 -07:00
72b18d378f coremem_types: Enum: add a method to set the enum to a specific variant 2022-07-21 21:46:33 -07:00
65f90d0654 coremem_types: Enum: implement mutable dispatch 2022-07-21 21:35:15 -07:00
e96f0db11a coremem_types: enum: verify that setting the discriminant works as expected 2022-07-21 21:16:43 -07:00
c889ec6d09 coremem_types: enum: simplify the discriminant trait impls 2022-07-21 21:08:41 -07:00
3541ab14c1 coremem_types: Enum: add a basic test 2022-07-21 20:35:30 -07:00
b8a36c87a6 coremem_types: enum: add helpers to encode a new discriminant
not used yet: i'll have to add a mutator-based dispatcher in order
to set the new variant's value when the discriminant is updated.
2022-07-21 18:40:09 -07:00
fba85c5ae3 coremem_types: list: add a set method 2022-07-21 18:36:11 -07:00
960804598a coremem_types: enums don't require their variant to be Copy
this is safe because the variant is necessarily not ZST.
if it was ZST it could just be stored explicitly instead of folding it
into the first element, and that case should still be OK.
2022-07-21 18:31:31 -07:00
9153dfbb7a coremem_types: enumerated: allow folding the enum into the first element 2022-07-21 18:28:20 -07:00
b15bad434e coremem_types: remove unused ElementAt type alias from list 2022-07-21 17:05:20 -07:00
f448f9200e implement an Enum type
right now the user has to define the method to extract the discriminant
from the enum.
i'll work to integrate the discriminant into the enum itself in future
patches.
2022-07-21 17:02:57 -07:00
d6ebf968b2 coremem_types: list: simplify the IntoList trait 2022-07-21 14:11:42 -07:00
90127e3f02 coremem_types: list: rename tuple_direct -> flat 2022-07-21 14:05:55 -07:00
8ce64ecc73 coremem_types: implement a List type which uses direct indexing, without consuming the whole list in the process
it's ugly, but it works and avoids spirv's ZST bugs.
i can clean it up a bit later (and rename it, since it's not actually
using tuples, directly).
2022-07-21 02:32:21 -07:00
86fb7f018d coremem_types: implement a linked-list based List type
it doesn't quite get us where we want to be, as it requires padding to
handle ZSTs in spirv. i think we HAVE to use a flat list (combined with
a copy method, instead of reference-based) to handle ZSTs without
forced padding.
2022-07-21 02:32:08 -07:00
2e3021b875 coremem_types: move list into a submodule so i can toy with alternate implementations 2022-07-20 23:51:58 -07:00
b555ee93f0 coremem_types: DiscrMat2: implement in terms of compound::list::List
i'm not happy AT ALL with this implementation,
but it's the first way i've found which works around ZST limitations.
2022-07-20 16:20:09 -07:00
d034453970 coremem_types: list: implement core::ops::Index 2022-07-20 14:48:52 -07:00
5b5085829d coremem_types: list: remove the dynamic indexing ops
trying to restructure this stuff specifically to aid the material needs.
2022-07-20 14:39:33 -07:00
7dd4b09faf coremem_types: list: remove unused test helpers 2022-07-20 14:37:54 -07:00
24b0b3d680 coremem_types: list: minor test refactoring 2022-07-20 14:37:08 -07:00
d0ae25e28b coremem_types: list: switch to parameter-based indexing; add apply method 2022-07-20 14:31:20 -07:00
e029c8b3d9 coremem_types: list: make the types public
in the future we can hopefully expose only a subset of the types.
2022-07-20 14:30:43 -07:00
5ddd6fef74 coremem_types: list: rename LLNode -> Node 2022-07-20 13:47:43 -07:00
0ce862b614 coremem_types: list: implement List building via a Chomp trait
it's simpler, and lets the user always do `(args...).into_list()`
on ANY `List<(Args...)>` alias type.
2022-07-20 13:46:17 -07:00
415ffb9c4d coremem_types: list: switch to 'head'/'tail' terminology 2022-07-20 13:10:14 -07:00
dfe27c9b56 coremem_types: list: add a way to construct the full list in one-go 2022-07-20 12:47:20 -07:00
00ae71a6eb coremem_types: add List::set, List::get_mut methods 2022-07-20 12:15:09 -07:00
18 changed files with 1192 additions and 558 deletions

View File

@@ -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));

View File

@@ -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 {

View File

@@ -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]

View File

@@ -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()
}
}

View File

@@ -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)
}
}

View File

@@ -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>;

View 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);
}
}

View File

@@ -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);
}
}

View 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>;
}

View 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);
}
}

View 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;
}

View 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);
// }
}

View File

@@ -1,2 +1,3 @@
pub mod enumerated;
pub mod list;
pub mod peano;

View File

@@ -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 { }

View 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());
}
}

View File

@@ -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);
}
}

View File

@@ -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 {

View File

@@ -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 {}