Skip to content

Instantly share code, notes, and snippets.

@AnthonyMikh
Last active August 29, 2023 21:25
Show Gist options
  • Save AnthonyMikh/50b4e384cf217e192b0b9af144684e26 to your computer and use it in GitHub Desktop.
Save AnthonyMikh/50b4e384cf217e192b0b9af144684e26 to your computer and use it in GitHub Desktop.
Как написать Fizzbuzz в .rodata
[package]
name = "fizzbuzz"
version = "0.1.0"
edition = "2021"
[features]
use_nightly = []
compare_with_previous_impl = []
#![cfg_attr(feature = "use_nightly", feature(generic_const_exprs), allow(incomplete_features))]
struct Z;
struct S<T>(T);
trait Add<Rhs> {
type Sum;
}
type SumOf<N, M> = <N as Add<M>>::Sum;
impl<N> Add<N> for Z {
type Sum = N;
}
impl<N, M> Add<M> for S<N>
where
N: Add<S<M>>,
{
type Sum = SumOf<N, S<M>>;
}
#[cfg(not(feature = "use_nightly"))]
type One = S<Z>;
#[cfg(not(feature = "use_nightly"))]
type Two = SumOf<One, One>;
#[cfg(not(feature = "use_nightly"))]
type Three = SumOf<One, Two>;
#[cfg(feature = "use_nightly")]
type Three = S<S<S<Z>>>;
#[cfg(not(feature = "use_nightly"))]
type Five = SumOf<Three, Two>;
#[cfg(feature = "use_nightly")]
type Five = S<S<S<S<S<Z>>>>>;
type Ten = SumOf<Five, Five>;
type TwentyFive = SumOf<Five, SumOf<Ten, Ten>>;
type Fifty = SumOf<TwentyFive, TwentyFive>;
type OneHundred = SumOf<Fifty, Fifty>;
type N = OneHundred;
struct Nil;
struct Cons<H, T>(H, T);
trait RangeDownFrom {
type Output;
}
impl RangeDownFrom for Z {
type Output = Cons<Z, Nil>;
}
impl<N> RangeDownFrom for S<N>
where
N: RangeDownFrom,
{
type Output = Cons<S<N>, N::Output>;
}
type MakeRangeDownFrom<N> = <N as RangeDownFrom>::Output;
trait ReverseWith<Acc> {
type Output;
}
impl<Acc> ReverseWith<Acc> for Nil {
type Output = Acc;
}
impl<Head, Tail, Acc> ReverseWith<Acc> for Cons<Head, Tail>
where
Tail: ReverseWith<Cons<Head, Acc>>,
{
type Output = <Tail as ReverseWith<Cons<Head, Acc>>>::Output;
}
type Reverse<List> = <List as ReverseWith<Nil>>::Output;
type RangeTo<N> = Reverse<MakeRangeDownFrom<N>>;
trait DecrementByMod<M> {
type Output;
}
impl<T> DecrementByMod<S<T>> for Z {
type Output = T;
}
impl<M, T> DecrementByMod<M> for S<T> {
type Output = T;
}
type DecMod<T, M> = <T as DecrementByMod<M>>::Output;
trait EnumerateFromWithCycle<Start, N> {
type Output;
}
type EnumeratedFromWithCycle<T, Start, N> = <T as EnumerateFromWithCycle<Start, N>>::Output;
impl<Start, N> EnumerateFromWithCycle<Start, N> for Nil {
type Output = Nil;
}
impl<Start, N, Head, Tail> EnumerateFromWithCycle<Start, N> for Cons<Head, Tail>
where
Start: DecrementByMod<N>,
Tail: EnumerateFromWithCycle<DecMod<Start, N>, N>,
{
type Output = Cons<(Head, Start), EnumeratedFromWithCycle<Tail, DecMod<Start, N>, N>>;
}
type EnumerateFromZeroWithCycle<T, N> = <T as EnumerateFromWithCycle<Z, N>>::Output;
type EnumerateFromZeroWithCycle3<T> = EnumerateFromZeroWithCycle<T, Three>;
type EnumerateFromZeroWithCycle5<T> = EnumerateFromZeroWithCycle<T, Five>;
type FizzBuzzEnumerate<T> = EnumerateFromZeroWithCycle5<EnumerateFromZeroWithCycle3<T>>;
struct Fizz;
struct Buzz;
struct FizzBuzz;
trait ToFizzBuzz {
type Output;
}
type FizzBuzzed<T> = <T as ToFizzBuzz>::Output;
impl<T> ToFizzBuzz for ((T, Z), Z) {
type Output = FizzBuzz;
}
impl<T, N> ToFizzBuzz for ((T, Z), S<N>) {
type Output = Fizz;
}
impl<T, N> ToFizzBuzz for ((T, S<N>), Z) {
type Output = Buzz;
}
impl<T, N, M> ToFizzBuzz for ((T, S<N>), S<M>) {
type Output = T;
}
impl ToFizzBuzz for Nil {
type Output = Nil;
}
impl<Head, Tail> ToFizzBuzz for Cons<Head, Tail>
where
Head: ToFizzBuzz,
Tail: ToFizzBuzz,
{
type Output = Cons<Head::Output, Tail::Output>;
}
trait TailOf {
type Output;
}
type Tail<T> = <T as TailOf>::Output;
impl<Head, Tail> TailOf for Cons<Head, Tail> {
type Output = Tail;
}
type FizzBuzzList<T> = FizzBuzzed<Tail<FizzBuzzEnumerate<RangeTo<T>>>>;
#[cfg(any(feature = "compare_with_previous_impl", not(feature = "use_nightly")))]
type List = FizzBuzzList<N>;
trait NumericValue {
const VALUE: usize;
}
impl NumericValue for Z {
const VALUE: usize = 0;
}
impl<N> NumericValue for S<N>
where
N: NumericValue,
{
const VALUE: usize = N::VALUE + 1;
}
#[cfg(feature = "compare_with_previous_impl")]
mod old_impl {
use super::*;
const fn to_str<const N: usize>(mut value: usize) -> Str<{ N }> {
let mut s = [0; N];
if value == 0 {
s[0] = b'0';
return unsafe { Str::from_parts_unchecked(s, 1) };
}
let mut i = 0;
while value > 0 {
s[i] = (value % 10) as u8 + b'0';
value /= 10;
i += 1;
}
let n_digits = i;
let mut i = 0;
while i < n_digits / 2 {
let digit = s[n_digits - i - 1];
s[n_digits - i - 1] = s[i];
s[i] = digit;
i += 1;
}
unsafe { Str::from_parts_unchecked(s, n_digits) }
}
impl<T, const N: usize> AsStr<{ N }> for T
where
T: NumericValue,
{
const VALUE: Str<{ N }> = to_str(<T as NumericValue>::VALUE);
}
pub trait AsStrList<const N: usize> {
type List;
const LIST: Self::List;
}
trait AsStr<const N: usize> {
const VALUE: Str<{ N }>;
}
impl<const N: usize> AsStr<{ N }> for Fizz {
const VALUE: Str<{ N }> = Str::from_literal("fizz");
}
impl<const N: usize> AsStr<{ N }> for Buzz {
const VALUE: Str<{ N }> = Str::from_literal("buzz");
}
impl<const N: usize> AsStr<{ N }> for FizzBuzz {
const VALUE: Str<{ N }> = Str::from_literal("fizzbuzz");
}
impl<const N: usize> AsStrList<{ N }> for Nil {
type List = Nil;
const LIST: Nil = Nil;
}
impl<Head, Tail, const N: usize> AsStrList<{ N }> for Cons<Head, Tail>
where
Head: AsStr<{ N }>,
Tail: AsStrList<{ N }>,
{
type List = Cons<Str<{ N }>, Tail::List>;
const LIST: Self::List = Cons(
<Head as AsStr<{ N }>>::VALUE,
<Tail as AsStrList<{ N }>>::LIST,
);
}
mod str {
pub struct Str<const N: usize>(StrInner<{ N }>);
enum StrInner<const N: usize> {
Plain(&'static str),
Decomposed([u8; N], usize),
}
impl<const N: usize> Str<{ N }> {
pub const fn from_literal(s: &'static str) -> Self {
Self(StrInner::Plain(s))
}
pub const unsafe fn from_parts_unchecked(bytes: [u8; N], len: usize) -> Self {
Self(StrInner::Decomposed(bytes, len))
}
pub fn as_str(&self) -> &str {
match &self.0 {
&StrInner::Plain(s) => s,
&StrInner::Decomposed(ref bytes, len) => unsafe {
std::str::from_utf8_unchecked(bytes.get_unchecked(..len))
},
}
}
}
}
use self::str::Str;
pub trait ForEach<Arg> {
fn for_each<F>(&self, f: F)
where
F: FnMut(&Arg);
}
impl<Arg> ForEach<Arg> for Nil {
fn for_each<F>(&self, _f: F)
where
F: FnMut(&Arg),
{
}
}
impl<Arg, Tail> ForEach<Arg> for Cons<Arg, Tail>
where
Tail: ForEach<Arg>,
{
fn for_each<F>(&self, mut f: F)
where
F: FnMut(&Arg),
{
f(&self.0);
self.1.for_each(f);
}
}
}
#[cfg(feature = "compare_with_previous_impl")]
use old_impl::*;
const fn n_digits(mut n: usize) -> usize {
if n == 0 {
return 1;
}
let mut ret = 0;
while n > 0 {
n /= 10;
ret += 1;
}
ret
}
const fn write_str_at<const N: usize>(
s: &str,
mut bytes: [u8; N],
mut at: usize,
) -> ([u8; N], usize) {
let mut i = s.len();
let s = s.as_bytes();
while i > 0 {
bytes[at - 1] = s[i - 1];
at -= 1;
i -= 1;
}
(bytes, at)
}
const DELIMITER: &str = "\n";
const fn write_str_with_delimiter_at<const N: usize>(
s: &str,
mut bytes: [u8; N],
mut at: usize,
) -> ([u8; N], usize) {
(bytes, at) = write_str_at(DELIMITER, bytes, at);
write_str_at(s, bytes, at)
}
const fn write_num_with_delimiter_at<const N: usize>(
mut n: usize,
mut bytes: [u8; N],
mut at: usize,
) -> ([u8; N], usize) {
(bytes, at) = write_str_at(DELIMITER, bytes, at);
if n == 0 {
bytes[at - 1] = b'0';
at -= 1;
return (bytes, at);
}
while n != 0 {
bytes[at - 1] = (n % 10) as u8 + b'0';
at -= 1;
n /= 10;
}
(bytes, at)
}
enum Repr {
Direct(&'static str),
Numeric(usize),
}
trait AsRepr {
const REPR: Repr;
}
impl<T: NumericValue> AsRepr for T {
const REPR: Repr = Repr::Numeric(T::VALUE);
}
impl AsRepr for Fizz {
const REPR: Repr = Repr::Direct("fizz");
}
impl AsRepr for Buzz {
const REPR: Repr = Repr::Direct("buzz");
}
impl AsRepr for FizzBuzz {
const REPR: Repr = Repr::Direct("fizzbuzz");
}
trait WriteLen {
const LEN: usize;
}
impl WriteLen for Nil {
const LEN: usize = 0;
}
impl<Head, Tail> WriteLen for Cons<Head, Tail>
where
Head: AsRepr,
Tail: WriteLen,
{
const LEN: usize = Tail::LEN
+ DELIMITER.len()
+ match Head::REPR {
Repr::Direct(s) => s.len(),
Repr::Numeric(n) => n_digits(n),
};
}
trait WriteBuf<const N: usize> {
const BUF: ([u8; N], usize);
}
impl<const N: usize> WriteBuf<{ N }> for Nil {
const BUF: ([u8; N], usize) = ([0; N], N);
}
impl<Head, Tail, const N: usize> WriteBuf<{ N }> for Cons<Head, Tail>
where
Head: AsRepr,
Tail: WriteBuf<{ N }>,
{
const BUF: ([u8; N], usize) = {
let (bytes, at) = Tail::BUF;
match Head::REPR {
Repr::Direct(s) => write_str_with_delimiter_at(s, bytes, at),
Repr::Numeric(n) => write_num_with_delimiter_at(n, bytes, at),
}
};
}
#[cfg(feature = "use_nightly")]
const fn to_buf<N>() -> ([u8; FizzBuzzList::<N>::LEN], usize)
where
N: RangeDownFrom,
MakeRangeDownFrom<N>: ReverseWith<Nil>,
RangeTo<N>: EnumerateFromWithCycle<Z, Three>,
EnumerateFromZeroWithCycle3<RangeTo<N>>: EnumerateFromWithCycle<Z, Five>,
FizzBuzzEnumerate<RangeTo<N>>: TailOf,
Tail<FizzBuzzEnumerate<RangeTo<N>>>: ToFizzBuzz,
FizzBuzzList<N>: WriteLen + WriteBuf<{ FizzBuzzList::<N>::LEN }>,
{
<FizzBuzzList::<N> as WriteBuf<{ FizzBuzzList::<N>::LEN }>>::BUF
}
const FIZZ_BUZZ_BYTES: &[u8] = &{
#[cfg(not(feature = "use_nightly"))]
let (precalculated, at) = {
const LIST_WRITE_LEN: usize = <List as WriteLen>::LEN;
<List as WriteBuf<LIST_WRITE_LEN>>::BUF
};
#[cfg(feature = "use_nightly")]
let (precalculated, at) = to_buf::<N>();
assert!(at == 0);
precalculated
};
#[cfg(feature = "compare_with_previous_impl")]
const FIZZ_BUZZ_STR: &str = unsafe { std::str::from_utf8_unchecked(FIZZ_BUZZ_BYTES) };
fn main() {
use std::io::Write;
#[cfg(feature = "compare_with_previous_impl")]
{
let mut printed = String::new();
<List as AsStrList<{ n_digits(<N as NumericValue>::VALUE) }>>::LIST.for_each(|s| {
printed += s.as_str();
printed += DELIMITER;
});
assert_eq!(printed.as_bytes(), FIZZ_BUZZ_STR);
}
std::io::stdout().write_all(FIZZ_BUZZ_BYTES).unwrap();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment