-rw-r--r--Cargo.lock56
-rw-r--r--Cargo.toml18
-rw-r--r--LICENSE21
-rw-r--r--README.md56
-rw-r--r--src/algebraic.rs63
-rw-r--r--src/fast.rs88
-rw-r--r--src/fast.safety.md4
-rw-r--r--src/lib.rs29
8 files changed, 335 insertions, 0 deletions
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..9fb0e37
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,56 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "lower"
+version = "0.1.3"
+dependencies = [
+ "lower-macros",
+]
+
+[[package]]
+name = "lower-macros"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fff64bae8cfa89c8871e4b07d27629aa000978b341b98ba5fc1a0a55e2a79a53"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.93"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.38"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.96"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11cd88e12b17c6494200a9c1b683a04fcac9573ed74cd1b62aeb2727c5592243"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..3e36934
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "lower"
+version = "0.1.3"
+authors = ["bend-n <[email protected]>"]
+description = "desugar math where the compiler wont"
+edition = "2021"
+repository = "https://github.com/bend-n/lower-plus.git"
+license = "MIT"
+
+[dependencies]
+lower-macros = "0.1.0"
+
+[features]
+default = ["modules"]
+modules = []
+
+[package.metadata.docs.rs]
+all-features = true
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..bf3f588
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2025 bendn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..2a1389f
--- /dev/null
+++ b/README.md
@@ -0,0 +1,56 @@
+# lower
+
+lowers expressions to their "desugared" form.
+
+e.g
+
+`a * b + c` => `(a.mul(b)).add(c)`
+
+note that it is _extremely pervasive_, so
+
+```rust
+lower::math! { fn main() -> u8 {
+ const X: u8 = 31 * 2;
+ return 2 * X + 2;
+} }
+```
+
+expands to
+
+```rust
+fn main() -> u8 {
+ const X: u8 = 31.mul(2);
+ return (2.mul(X)).add(2);
+}
+```
+
+it should work for most expressions.
+
+also implements some modules that let it work with some core intrinsics (`f*_fast`, `f*_algebraic`). (nightly only!)
+
+## why
+
+rust added an amazing feature called algebraic math (thanks orlp), allowing us to use `f*_algebraic`. however, dont you hate it when your
+
+```rs
+fn madd<const N: usize>(x: [[f32; 3]; N]) -> [f32; N] {
+ x.map(|[a, b, c]| a * b + c) // not optimized! cant use `(v)fmadd`
+}
+```
+
+turns into
+
+```rs
+fn madd<const N: usize>(x: [[f32; 3]; N]) -> [f32; N] {
+ x.map(|[a, b, c]| core::intrinsics::fadd_algebraic(core::intrinsics::fmul_algebraic(a, b), c)) // readability in shambles
+}
+```
+
+this crate allows you to
+
+```rs
+fn madd<const N: usize>(x: [[f32; 3]; N]) -> [f32; N] {
+ // wow! such readability! ultimate simd!
+ lower::algebraic! { x.map(|[a, b, c]| a * b + c) }
+}
+```
diff --git a/src/algebraic.rs b/src/algebraic.rs
new file mode 100644
index 0000000..a7d6082
--- /dev/null
+++ b/src/algebraic.rs
@@ -0,0 +1,63 @@
+//! Provides the algebraic math trait and macro.
+/// Trait for Algebraic-ly math'd floats. Try to use [`algebraic::math`](`math`).
+pub trait Float: Copy {
+ fn add(self, other: Self) -> Self;
+ fn sub(self, other: Self) -> Self;
+ fn mul(self, other: Self) -> Self;
+ fn div(self, other: Self) -> Self;
+ fn rem(self, other: Self) -> Self;
+
+ fn eq(self, other: Self) -> bool;
+ fn lt(self, other: Self) -> bool;
+ fn le(self, other: Self) -> bool;
+ fn ne(self, other: Self) -> bool;
+ fn ge(self, other: Self) -> bool;
+ fn gt(self, other: Self) -> bool;
+
+ fn deref(&self) -> Self {
+ *self
+ }
+ fn neg(self) -> Self;
+}
+use std::intrinsics::*;
+macro_rules! imp {
+ ($this:ty) => {
+#[rustfmt::skip]
+impl Float for $this {
+ fn add(self, other: Self) -> Self { fadd_algebraic(self, other) }
+ fn sub(self, other: Self) -> Self { fsub_algebraic(self, other) }
+ fn mul(self, other: Self) -> Self { fmul_algebraic(self, other) }
+ fn div(self, other: Self) -> Self { fdiv_algebraic(self, other) }
+ fn rem(self, other: Self) -> Self { frem_algebraic(self, other) }
+
+ fn eq(self, other: Self) -> bool { self == other }
+ fn lt(self, other: Self) -> bool { self < other }
+ fn le(self, other: Self) -> bool { self <= other }
+ fn ne(self, other: Self) -> bool { self != other }
+ fn ge(self, other: Self) -> bool { self >= other }
+ fn gt(self, other: Self) -> bool { self > other }
+
+ fn deref(&self) -> Self { *self }
+ fn neg(self) -> Self { -self }
+}
+ };
+}
+imp!(f32);
+imp!(f64);
+
+/// Changes all the math to algebraic float math. See [`fadd_algebraic`].
+///
+/// This allows automatic vectorization of float math easier for the compiler, among other things.
+/// If you intend to create a function in this, first import [`lower::algebraic::Float`](crate::algebraic::Float), use [`lower::math`](crate::math).
+///
+/// This would lower `a + b + c + d` to `alge_add(alge_add(alge_add(a, b), c), d)` (which could be organized by the compiler into `(a + b) + (c + d)` if it chooses)
+#[macro_export]
+#[doc(hidden)]
+macro_rules! algebraic {
+ ($($x:tt)+) => {{
+ use $crate::algebraic::Float as _;
+ lower_macros::math! { $($x)+ }
+ }}
+}
+#[doc(inline)]
+pub use algebraic as math;
diff --git a/src/fast.rs b/src/fast.rs
new file mode 100644
index 0000000..e375172
--- /dev/null
+++ b/src/fast.rs
@@ -0,0 +1,88 @@
+//! Provides the fast math trait and macro. See terms and conditions[^1].
+//!
+//! [^1]: <https://simonbyrne.github.io/notes/fastmath>
+#[allow(unused_imports)]
+use std::f32::{INFINITY as INF, NAN};
+
+macro_rules! s {
+ ($($x:tt)+) => {
+ #[doc = include_str!("fast.safety.md")]
+ $($x)+;
+ };
+}
+
+#[rustfmt::skip]
+/// Trait for fast math floats. Try to use [`fast::math`](crate::fast::math).
+/// These functions all assume that your float is NOT [`INF`] | [`NAN`].
+/// If so undefined behaviour is incurred.
+pub unsafe trait Float: Copy {
+ s!(unsafe fn add(self, other: Self) -> Self);
+ s!(unsafe fn sub(self, other: Self) -> Self);
+ s!(unsafe fn mul(self, other: Self) -> Self);
+ s!(unsafe fn div(self, other: Self) -> Self);
+ s!(unsafe fn rem(self, other: Self) -> Self);
+
+ s!(unsafe fn eq(self, other: Self) -> bool);
+ s!(unsafe fn lt(self, other: Self) -> bool);
+ s!(unsafe fn le(self, other: Self) -> bool);
+ s!(unsafe fn ne(self, other: Self) -> bool);
+ s!(unsafe fn ge(self, other: Self) -> bool);
+ s!(unsafe fn gt(self, other: Self) -> bool);
+
+ fn deref(&self) -> Self {
+ *self
+ }
+ fn neg(self) -> Self;
+
+ #[doc(hidden)]
+ unsafe fn finite(self, other: Self) -> Self;
+}
+
+use std::intrinsics::*;
+macro_rules! imp {
+ ($this:ty) => {
+#[rustfmt::skip]
+unsafe impl Float for $this {
+ #[inline(always)]
+ unsafe fn finite(self, other: Self) -> Self {
+ core::hint::assert_unchecked(self.is_finite());
+ core::hint::assert_unchecked(other.is_finite());
+ self
+ }
+ unsafe fn add(self, other: Self) -> Self { fadd_fast(self.finite(other), other) }
+ unsafe fn sub(self, other: Self) -> Self { fsub_fast(self.finite(other), other) }
+ unsafe fn mul(self, other: Self) -> Self { fmul_fast(self.finite(other), other) }
+ unsafe fn div(self, other: Self) -> Self { fdiv_fast(self.finite(other), other) }
+ unsafe fn rem(self, other: Self) -> Self { frem_fast(self.finite(other), other) }
+
+ unsafe fn eq(self, other: Self) -> bool { (self.finite(other) + 0.0).to_bits() == (other + 0.0).to_bits() }
+ unsafe fn lt(self, other: Self) -> bool { self.finite(other) < other }
+ unsafe fn le(self, other: Self) -> bool { self.finite(other) <= other }
+ unsafe fn ne(self, other: Self) -> bool { self.finite(other) != other }
+ unsafe fn ge(self, other: Self) -> bool { self.finite(other) >= other }
+ unsafe fn gt(self, other: Self) -> bool { self.finite(other) > other }
+
+ fn deref(&self) -> Self { *self }
+ fn neg(self) -> Self { -self }
+}
+ };
+}
+imp!(f32);
+imp!(f64);
+
+/// Changes all the math to fast float math. See [`fadd_fast`].
+///
+/// This allows automatic vectorization of float math for the compiler, among other things.
+/// If you intend to create a function in this, first import [`lower::fast::Float`](crate::fast::Float), use [`lower::math`](crate::math).
+///
+/// This would lower `a * b + c` to `fadd(fmul(a, b), c)`.
+#[macro_export]
+#[doc(hidden)]
+macro_rules! fast {
+ ($($x:tt)+) => {{
+ use $crate::fast::Float as _;
+ lower_macros::math! { $($x)+ }
+ }}
+}
+#[doc(inline)]
+pub use fast as math;
diff --git a/src/fast.safety.md b/src/fast.safety.md
new file mode 100644
index 0000000..115a9e2
--- /dev/null
+++ b/src/fast.safety.md
@@ -0,0 +1,4 @@
+# Safety
+
+MUSNT call this function with [`NAN`] | [`INF`].
+This function WILL assume the inputs are finite.
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..73e8dce
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,29 @@
+//! a lil macro crate.
+//!
+//! provides a handy macro for converting `a + b` to `a.add(b)` for when you cant easily overload the `Add` trait,
+//! and some traits that provide [`f*_algebraic`](algebraic::math) and [`f*_fast`](fast::math) implementations. (requires nightly)
+#![allow(internal_features)]
+#![cfg_attr(
+ feature = "modules",
+ feature(doc_auto_cfg, core_intrinsics, hint_assert_unchecked)
+)]
+
+#[cfg(feature = "modules")]
+pub mod algebraic;
+#[cfg(feature = "modules")]
+pub mod fast;
+
+/// Lower math to method calls. Only useful if you define the functions.
+/// ```
+/// # use std::ops::*;
+/// let [a, b, c] = [5i32, 6, 7];
+/// assert_eq!(lower::math! { a * *&b + -c }, a * *&b + -c);
+/// // expands to
+/// // a.mul((&b).deref()).add(c.neg())
+/// ```
+#[macro_export]
+macro_rules! math {
+ ($($x:tt)+) => {
+ lower_macros::math! { $($x)+ }
+ };
+}