From 253ee34a915ab9e228fa8b36e799593063d001ea Mon Sep 17 00:00:00 2001 From: Avril Date: Sat, 17 Dec 2022 08:15:14 +0000 Subject: [PATCH] day14: reworked (rust version.) parsing: seems correct~ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fortune for day14's current commit: Small blessing − 小吉 --- .gitignore | 4 + day14/Cargo.lock | 140 +++++++++++++++++++++++ day14/Cargo.toml | 21 ++++ day14/Makefile | 117 -------------------- day14/include/input.h | 39 ------- day14/src/input.cpp | 120 -------------------- day14/src/main.rs | 250 ++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 415 insertions(+), 276 deletions(-) create mode 100644 day14/Cargo.lock create mode 100644 day14/Cargo.toml delete mode 100644 day14/Makefile delete mode 100644 day14/include/input.h delete mode 100644 day14/src/input.cpp create mode 100644 day14/src/main.rs diff --git a/.gitignore b/.gitignore index bd1b4d5..7295143 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,7 @@ *-debug **/input **/target/ + +perf.* +flamegraph.svg +*.png diff --git a/day14/Cargo.lock b/day14/Cargo.lock new file mode 100644 index 0000000..906d2fc --- /dev/null +++ b/day14/Cargo.lock @@ -0,0 +1,140 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "backtrace" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "cc" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "color-eyre" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204" +dependencies = [ + "backtrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", +] + +[[package]] +name = "day14" +version = "0.1.0" +dependencies = [ + "color-eyre", + "linebuffer", +] + +[[package]] +name = "eyre" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "gimli" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec7af912d60cdbd3677c1af9352ebae6fb8394d165568a2234df0fa00f87793" + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "libc" +version = "0.2.138" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" + +[[package]] +name = "linebuffer" +version = "0.1.0" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + +[[package]] +name = "object" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "239da7f290cfa979f43f85a8efeee9a8a76d0827c356d37f9d3d7254d6b537fb" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" + +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + +[[package]] +name = "rustc-demangle" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" diff --git a/day14/Cargo.toml b/day14/Cargo.toml new file mode 100644 index 0000000..4c81545 --- /dev/null +++ b/day14/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "day14" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[profile.release] +opt-level = 3 +lto = "fat" +codegen-units = 1 +strip=true + +[profile.symbols] +inherits="release" +#incremental=true +strip=false + +[dependencies] +color-eyre = { version = "0.6.2", default-features = false } +linebuffer = { path = "/home/avril/work/linebuffer" } diff --git a/day14/Makefile b/day14/Makefile deleted file mode 100644 index c404de2..0000000 --- a/day14/Makefile +++ /dev/null @@ -1,117 +0,0 @@ -# Generic C and C++ Makefile project template -# Contains targets for `release', `debug', and `clean'. - -PROJECT=project -AUTHOR=Avril (Flanchan) -VERSION=0.0.0 - -SRC_C = $(wildcard src/*.c) -SRC_CXX = $(wildcard src/*.cpp) - -INCLUDE=include - -# Link to these libraries dynamicalls -SHARED_LIBS= -# Link to these libraries statically -STATIC_LIBS= - -override __VERSION_SPLIT:= $(subst ., ,$(VERSION)) -override __VERSION_REVISION:=$(word 3,$(__VERSION_SPLIT)) 0 -override __VERSION_SPLIT:= MAJOR:$(word 1,$(__VERSION_SPLIT)) MINOR:$(word 2,$(__VERSION_SPLIT)) BUGFIX:$(word 1,$(subst r, ,$(__VERSION_REVISION))) REVISION:$(word 2,$(subst r, ,$(__VERSION_REVISION))) REVISION_STRING:$(word 3,$(__VERSION_SPLIT)) - -COMMON_FLAGS+= -W -Wall -Wstrict-aliasing -fno-strict-aliasing $(addprefix -I,$(INCLUDE)) -COMMON_FLAGS+= $(addprefix -D_VERSION_,$(subst :,=,$(__VERSION_SPLIT))) '-D_VERSION="$(VERSION)"' - -# Target arch. Set to blank for generic -ARCH?=native -# Enable OpenMP and loop parallelisation? (dyn-links to openmp) -PARALLEL?=yes - -OPT_FLAGS?= -fgraphite \ - -floop-interchange -ftree-loop-distribution -floop-strip-mine -floop-block \ - -fno-stack-check - -ifneq ($(ARCH),) - OPT_FLAGS+= $(addprefix -march=,$(ARCH)) -endif - -ifeq ($(PARALLEL),yes) - OPT_FLAGS+= -fopenmp -floop-parallelize-all -ftree-parallelize-loops=4 -endif - -CXX_OPT_FLAGS?= $(OPT_FLAGS) -felide-constructors - -CSTD?=gnu2x -CXXSTD?=gnu++23 - -CFLAGS += $(COMMON_FLAGS) --std=$(CSTD) -CXXFLAGS += $(COMMON_FLAGS) --std=$(CXXSTD) -LDFLAGS += $(addsuffix .a,$(addprefix -l:lib,$(STATIC_LIBS))) $(addprefix -l,$(SHARED_LIBS)) - - -STRIP=strip - -RELEASE_COMMON_FLAGS+= -Werror -fno-bounds-check -DEBUG_COMMON_FLAGS+= -ggdb -gz -fanalyzer -ftrapv -fbounds-check - -ifneq ($(TARGET_SPEC_FLAGS),no) - RELEASE_CFLAGS?= -O3 -flto $(OPT_FLAGS) - RELEASE_CXXFLAGS?= -O3 -flto $(CXX_OPT_FLAGS) - RELEASE_LDFLAGS?= -Wl,-O3 -Wl,-flto - - DEBUG_CFLAGS?= -Og - DEBUG_CXXFLAGS?= -Og - - DEBUG_LDFLAGS?= -endif - -DEBUG_CFLAGS+=-DDEBUG $(DEBUG_COMMON_FLAGS) -DEBUG_CXXFLAGS+=-DDEBUG $(DEBUG_COMMON_FLAGS) -fasynchronous-unwind-tables - -RELEASE_CFLAGS+=-DRELEASE $(RELEASE_COMMON_FLAGS) -RELEASE_CXXFLAGS+=-DRELEASE $(RELEASE_COMMON_FLAGS) - -# Objects - -OBJ_C = $(addprefix obj/c/,$(SRC_C:.c=.o)) -OBJ_CXX = $(addprefix obj/cxx/,$(SRC_CXX:.cpp=.o)) -OBJ = $(OBJ_C) $(OBJ_CXX) - -# Phonies - -.PHONY: release -release: | dirs $(PROJECT)-release - -.PHONY: debug -debug: | dirs $(PROJECT)-debug - -# Targets - -dirs: - @mkdir -p obj/c{,xx}/src - -obj/c/%.o: %.c - $(CC) -c $< $(CFLAGS) -o $@ $(LDFLAGS) - -obj/cxx/%.o: %.cpp - $(CXX) -c $< $(CXXFLAGS) -o $@ $(LDFLAGS) - -$(PROJECT)-release: CFLAGS+= $(RELEASE_CFLAGS) -$(PROJECT)-release: CXXFLAGS += $(RELEASE_CXXFLAGS) -$(PROJECT)-release: LDFLAGS += $(RELEASE_LDFLAGS) -$(PROJECT)-release: $(OBJ) - $(CXX) $^ $(CXXFLAGS) -o $@ $(LDFLAGS) - $(STRIP) $@ - -$(PROJECT)-debug: CFLAGS+= $(DEBUG_CFLAGS) -$(PROJECT)-debug: CXXFLAGS += $(DEBUG_CXXFLAGS) -$(PROJECT)-debug: LDFLAGS += $(DEBUG_LDFLAGS) -$(PROJECT)-debug: $(OBJ) - $(CXX) $^ $(CXXFLAGS) -o $@ $(LDFLAGS) - -clean-rebuild: - rm -rf obj - -clean: clean-rebuild - rm -f $(PROJECT)-{release,debug,pgo} - diff --git a/day14/include/input.h b/day14/include/input.h deleted file mode 100644 index a3d5204..0000000 --- a/day14/include/input.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include - -#include - -struct Point { - uint64_t x,y; -}; - -struct Line { - Point start, end; -}; - -template -using Output = T* __restrict__; - -namespace Input { - struct Parser { - Parser(const char* string, size_t len) noexcept; - template - inline Parser(const char (&string)[N]) noexcept - : Parser(string, N) {} - - Parser(Parser&&) noexcept; - Parser(const Parser&) = delete; - - Parser& operator=(Parser&&) noexcept; - Parser& operator=(const Parser&) = delete; - - bool try_read_next(Output); - - virtual ~Parser(); - private: - struct _impl; - - std::unique_ptr<_impl> state_; - }; -} diff --git a/day14/src/input.cpp b/day14/src/input.cpp deleted file mode 100644 index 200457b..0000000 --- a/day14/src/input.cpp +++ /dev/null @@ -1,120 +0,0 @@ - -#include - -#include - -namespace { - template - [[gnu::pure]] - inline T* gmemchr(T* ptr, char ch, size_t sz) noexcept { - return (T*)memchr((void*)ptr, ch, sz * sizeof(T)); - } - - template - inline size_t gmemchr_s(T* ptr, char ch, size_t sz) noexcept { - if(T* p = gmemchr(ptr, ch, sz)) return (size_t)(p - ptr); - else return 0; - } - - template requires(std::is_invocable_v) - struct Defer { - constexpr explicit Defer(std::nullptr_t) noexcept - : f(nullptr) {} - constexpr Defer(F&& func) noexcept(std::is_nothrow_move_constructible_v) - : f(std::make_unique(std::move(func))) {} - constexpr virtual ~Defer() noexcept(std::is_nothrow_invocable_v) { if(f) (*f)(); } - - constexpr Defer(Defer&& m) noexcept - : f(std::move(m.f)) {} - constexpr Defer(const Defer& p) noexcept requires(std::is_copy_constructible_v) - : f(p.f ? *p.f : nullptr) {} - constexpr Defer& operator=(Defer&& m) noexcept(std::is_nothrow_move_constructible_v) - { - if(this != std::addressof(m)) { - f = std::move(m.f); - } return *this; - } - constexpr Defer& operator=(const Defer& c) noexcept(std::is_nothrow_copy_constructible_v) requires(std::is_copy_constructible_v) - { - if(this != std::addressof(m)) { - if(c.f) f = std::make_unique(*c.f); - else f = nullptr; - } return *this; - } - - template - constexpr decltype(auto) operator()(Args&&... args) noexcept(std::is_nothrow_invocable_v) requires(std::is_invocable_v) - { return std::forward>( (*f)() ); } - private: - std::unique_ptr f; - }; - template requires(std::is_invocable_v) - constexpr auto defer_call(F&& func) noexcept(std::is_nothrow_move_constructible_v) { return Defer{std::move(func)}; } -#define $CAT_(a, b) a ## b -#define $CAT(a, b) $CAT_(a, b) -#define $DEFER(...) const auto $CAT(_$__defer_, __LINE__) = ::defer_call( __VA_ARGS__ ) -} - -namespace Input { - struct Parser::_impl { - struct { - const char* data; - size_t len; - } src; - - const char* cur_line; - size_t cur_len; - }; - Parser::Parser(const char* string, size_t len) noexcept - : state_(std::make_unique<_impl>()) - { - state_->cur_line = state_->src.data = string; - state_->cur_len = state_->src.len = len; - } - - Parser::~Parser() {} - - Parser::Parser(Parser&& p) noexcept - : state_(std::move(p.state_)) {} - - Parser& Parser::operator=(Parser&& p) noexcept - { - if(this != std::addressof(p)) { - state_ = std::move(p.state_); - } return *this; - } - - bool Parser::try_read_next(Output output) - { - const char* start = state_->cur_line; - const char* end = gmemchr(start, '\n', state_->cur_len); - if(!end) return false; - - size_t diff = (size_t)(end - state_->cur_line); - - if(diff > 1) { - auto string = std::string_view{ start, gmemchr_s(start, ' ', diff - 1) }; - - for(size_t i=0; - string.size() && string.data() < end; - i++, - string = std::string_view{ start += string.size(), // start = start + last size - gmemchr_s(start, ' ', diff - 1) // XXX: <-- this should be `start + string.size()` - }) - { - switch(i) { - case 0: output->start = parse_point(string); break; // First point - case 1: continue; // Arrow - case 2: output->end = parse_point(string); break; // Second point - case 3: continue; // Arrow - default: goto le; // Next point, quit - } - } - le: - end = ((const char*)string.data()) + string.size(); - state_->cur_len = (size_t)(end - state_->cur_line); // TODO: Is this the correct length calculation? I don't think it is.... - state_->cur_line = end; - return true; - } else return false; - } -} diff --git a/day14/src/main.rs b/day14/src/main.rs new file mode 100644 index 0000000..ee6e8c8 --- /dev/null +++ b/day14/src/main.rs @@ -0,0 +1,250 @@ +#![feature(repr_simd)] + +use std::{ + str, + io, + iter::{ + self, + Fuse, + }, + path::Path, +}; + +use color_eyre::{ + eyre::{ + self, eyre, + WrapErr as _, + }, + Help as _, SectionExt as _, +}; + +use linebuffer::{ + FromBuf, TryFromBuf, + ParsedLines, + buf::forward, +}; + +mod ext { + use super::iter::{ + self, + FusedIterator, + }; + + #[derive(Debug, Clone)] + pub struct TakeTwo(I); + + impl Iterator for TakeTwo + where I: Iterator + { + type Item = (T, Option); + #[inline] + fn next(&mut self) -> Option + { + let first = self.0.next()?; + Some((first, self.0.next())) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let (low, high) = self.0.size_hint(); + (low / 2, high.map(|x| x/2)) + } + } + + impl ExactSizeIterator for TakeTwo{} + impl FusedIterator for TakeTwo {} + + pub trait TakeTwoExt: Sized { + fn take_two(self) -> TakeTwo; + } + pub trait TakeTwoBoxedExt { + fn take_two(self: Box) -> TakeTwo>; + } + + impl TakeTwoExt for I + { + #[inline(always)] + fn take_two(self) -> TakeTwo { + TakeTwo(self) + } + } + + impl TakeTwoBoxedExt for I + { + #[inline(always)] + fn take_two(self: Box) -> TakeTwo> { + TakeTwo(self) + } + } + +} +use ext::{ + TakeTwoExt as _, + TakeTwoBoxedExt as _, +}; + +/// A point is a vector +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy)] +#[repr(simd)] +struct Point { + pub x: u64, + pub y: u64, +} + +impl str::FromStr for Point +{ + type Err = ::Err; + #[inline] + fn from_str(s: &str) -> Result { + let (x, y) = s.split_once(',').unwrap_or((s, "0")); + Ok(Self { + x: x.parse()?, + y: y.parse()?, + }) + } +} + +/// A `Line` is potentially two X any Ys +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy)] +struct Line { + start: Point, + end: Point, +} + +/// A single parsed line into `Line`s. +#[derive(Debug, Clone, Default)] +struct Lines { + lines: Vec, +} + +impl IntoIterator for Lines +{ + type Item= Line; + type IntoIter = std::vec::IntoIter; + + #[inline] + fn into_iter(self) -> Self::IntoIter + { + self.lines.into_iter() + } +} + + +impl Lines { + #[inline] + pub fn concat(mut self, next: impl IntoIterator) -> Self + { + self.lines.extend(next); + self + } + #[inline] + pub fn flatten(lines: impl IntoIterator) -> Self + { + Self { + lines: lines.into_iter().map(|x| x.lines.into_iter()).flatten().collect(), + } + } +} + +impl str::FromStr for Lines +{ + type Err = ::Err; + #[inline] + fn from_str(s: &str) -> Result { + let mut next = s.split(" -> ").peekable(); + let mut lines = Vec::new(); + while let Some(start) = next.next() + { + let Some(second) = next.peek() else { continue }; + lines.push(Line { + start: start.parse()?, + end: second.parse()? + }) + } + Ok(Self{lines}) + } +} + +#[derive(Debug)] +struct Input { + all: Lines, +} + +impl Input +{ + /// Size of all non-unzipped (H+V) lines + #[inline] + pub fn zipped_len(&self) -> usize + { + self.all.lines.len() + } +} + +impl FromIterator for Input +{ + #[inline] + fn from_iter>(iter: I) -> Self + { + Self{ all: Lines::flatten(iter.into_iter()) } + } +} + + +impl Input { + #[inline(always)] + pub fn parse(reader: R) -> eyre::Result + { + Self::from_parser(ParsedLines::new(reader)) + } + pub fn from_parser(lines: ParsedLines>) -> eyre::Result + { + struct ParsedIter(ParsedLines>); + impl Iterator for ParsedIter + { + type Item = Result>; + #[inline(always)] + fn next(&mut self) -> Option + { + self.0.try_next() + } + } + + ParsedIter(lines).enumerate().map(|(i,x)| x.wrap_err("Failed to parse single line").with_section(move || i.header("Line number is"))).collect() + } +} + +#[inline] +fn load_input_from(from: impl AsRef) -> eyre::Result +{ + std::fs::OpenOptions::new() + .read(true) + .open(&from) + .with_section(|| format!("{:?}", from.as_ref()).header("File name was")) +} + +/// Input filename +const INPUT_FILENAME: &'static str = "input"; +/// Starting point of sand falling +const ORIGIN_POINT: Point = Point { x: 500, y: 0 }; + +fn main() -> eyre::Result<()> { + color_eyre::install()?; + + // Input + let input = load_input_from(INPUT_FILENAME) + .wrap_err("Failed to open input file")?; + + // Parse input + let input = Input::parse(input) + .wrap_err("Failed to parse input")?; + + //TODO: Deconstruct each `Line` into the horizontal and vertical movements + //eprintln!("{input:#?}"); + println!("{}", input.zipped_len()); + + //TODO: Draw lines into grid before starting simulation of sand from `ORIGIN_POINT`. + + //TODO: Simulate falling, adding and counting each stopped particle until the sand drops below the bottom of the lowest vertical line. + + Ok(()) +}