Skip to content

Commit

Permalink
banger
Browse files Browse the repository at this point in the history
  • Loading branch information
remi-dupre committed Feb 5, 2025
1 parent 6ebcf2d commit 5fadabd
Show file tree
Hide file tree
Showing 15 changed files with 374 additions and 261 deletions.
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## 1.1.0

### General

- Allow to simplify "canonical" expressions (expressions expressed as simple
intervals over each dimension).

## 1.0.2

### Python
Expand All @@ -15,7 +22,7 @@ That's not really a huge milestone, but:

### General

- Add easter support
- Add Easter support

## 0.11.1

Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "opening-hours"
version = "1.0.2"
version = "1.1.0"
authors = ["Rémi Dupré <remi@dupre.io>"]
license = "MIT OR Apache-2.0"
readme = "README.md"
Expand Down Expand Up @@ -34,9 +34,9 @@ disable-test-timeouts = []

[dependencies]
chrono = "0.4"
compact-calendar = { path = "compact-calendar", version = "1.0.2" }
compact-calendar = { path = "compact-calendar", version = "1.1.0" }
flate2 = "1.0"
opening-hours-syntax = { path = "opening-hours-syntax", version = "1.0.2" }
opening-hours-syntax = { path = "opening-hours-syntax", version = "1.1.0" }
sunrise-next = "1.2"

# Feature: log (default)
Expand All @@ -51,7 +51,7 @@ tzf-rs = { version = "0.4", default-features = false, optional = true }

[build-dependencies]
chrono = "0.4"
compact-calendar = { path = "compact-calendar", version = "1.0.2" }
compact-calendar = { path = "compact-calendar", version = "1.1.0" }
country-boundaries = { version = "1.2", optional = true }
flate2 = "1.0"
rustc_version = "0.4.0"
Expand Down
2 changes: 1 addition & 1 deletion compact-calendar/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "compact-calendar"
version = "1.0.2"
version = "1.1.0"
authors = ["Rémi Dupré <remi@dupre.io>"]
license = "MIT OR Apache-2.0"
readme = "README.md"
Expand Down
6 changes: 3 additions & 3 deletions opening-hours-py/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "opening-hours-py"
version = "1.0.2"
version = "1.1.0"
authors = ["Rémi Dupré <remi@dupre.io>"]
license = "MIT OR Apache-2.0"
readme = "README.md"
Expand All @@ -23,12 +23,12 @@ pyo3-stub-gen = "0.7"
[dependencies.opening-hours-rs]
package = "opening-hours"
path = ".."
version = "1.0.2"
version = "1.1.0"
features = ["log", "auto-country", "auto-timezone"]

[dependencies.opening-hours-syntax]
path = "../opening-hours-syntax"
version = "1.0.2"
version = "1.1.0"
features = ["log"]

[dependencies.pyo3]
Expand Down
2 changes: 1 addition & 1 deletion opening-hours-syntax/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "opening-hours-syntax"
version = "1.0.2"
version = "1.1.0"
authors = ["Rémi Dupré <remi@dupre.io>"]
license = "MIT OR Apache-2.0"
readme = "README.md"
Expand Down
77 changes: 52 additions & 25 deletions opening-hours-syntax/src/rubik.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub type Paving5D<T, U, V, W, X> = Dim<T, Paving4D<U, V, W, X>>;
pub(crate) enum PavingSelector<T, U> {
Empty,
// TODO: vec
Dim { range: Range<T>, tail: U },
Dim { range: Vec<Range<T>>, tail: U },
}

impl PavingSelector<(), ()> {
Expand All @@ -21,11 +21,14 @@ impl PavingSelector<(), ()> {
}

impl<T, U> PavingSelector<T, U> {
pub(crate) fn dim<K>(self, range: Range<K>) -> PavingSelector<K, PavingSelector<T, U>> {
PavingSelector::Dim { range, tail: self }
pub(crate) fn dim<K>(
self,
range: impl Into<Vec<Range<K>>>,
) -> PavingSelector<K, PavingSelector<T, U>> {
PavingSelector::Dim { range: range.into(), tail: self }
}

pub(crate) fn unpack(&self) -> (&Range<T>, &U) {
pub(crate) fn unpack(&self) -> (&[Range<T>], &U) {
let Self::Dim { range, tail } = &self else {
panic!("tried to unpack empty selector");
};
Expand Down Expand Up @@ -138,52 +141,76 @@ impl<T: Clone + Ord, U: Default + Paving> Paving for Dim<T, U> {
}

fn set(&mut self, selector: &Self::Selector, val: bool) {
let (range, selector_tail) = selector.unpack();
self.cut_at(range.start.clone());
self.cut_at(range.end.clone());
let (ranges, selector_tail) = selector.unpack();

for (col_start, col_val) in self.cuts.iter().zip(&mut self.cols) {
if *col_start >= range.start && *col_start < range.end {
col_val.set(selector_tail, val);
for range in ranges {
self.cut_at(range.start.clone());
self.cut_at(range.end.clone());

for (col_start, col_val) in self.cuts.iter().zip(&mut self.cols) {
if *col_start >= range.start && *col_start < range.end {
col_val.set(selector_tail, val);
}
}
}
}

fn is_val(&self, selector: &Self::Selector, val: bool) -> bool {
let (range, selector_tail) = selector.unpack();

for ((col_start, col_end), col_val) in self.cuts.iter().zip(&self.cuts[1..]).zip(&self.cols)
{
// TODO: don't I miss something?
if *col_start < range.end
&& *col_end > range.start
&& !col_val.is_val(selector_tail, val)
let (ranges, selector_tail) = selector.unpack();

for range in ranges {
if range.start < range.end && self.cols.is_empty() {
return !val;
}

for ((col_start, col_end), col_val) in self
.cuts
.iter()
.zip(self.cuts.iter().skip(1))
.zip(&self.cols)
{
return false;
// TODO: don't I miss something?
if *col_start < range.end
&& *col_end > range.start
&& !col_val.is_val(selector_tail, val)
{
return false;
}
}
}

true
}

fn pop_selector(&mut self) -> Option<Self::Selector> {
let (start_idx, selector_tail) = self
let (mut start_idx, selector_tail) = self
.cols
.iter_mut()
.enumerate()
.find_map(|(idx, col)| Some((idx, col.pop_selector()?)))?;

let mut end_idx = start_idx + 1;
let mut selector_range = Vec::new();

while end_idx < self.cols.len() {
if self.cols[end_idx].is_val(&selector_tail, true) {
end_idx += 1;
continue;
}

if start_idx < end_idx {
selector_range.push(self.cuts[start_idx].clone()..self.cuts[end_idx].clone());
}

while end_idx < self.cols.len() && self.cols[end_idx].is_val(&selector_tail, true) {
end_idx += 1;
start_idx = end_idx;
}

let selector = PavingSelector::Dim {
range: self.cuts[start_idx].clone()..self.cuts[end_idx].clone(),
tail: selector_tail,
};
if start_idx < end_idx {
selector_range.push(self.cuts[start_idx].clone()..self.cuts[end_idx].clone());
}

let selector = PavingSelector::Dim { range: selector_range, tail: selector_tail };
self.set(&selector, false);
Some(selector)
}
Expand Down
1 change: 0 additions & 1 deletion opening-hours-syntax/src/rules/day.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,6 @@ impl Display for WeekDayRange {
.map(|(idx, _)| -(idx as isize) - 1);

let mut weeknum_iter = pos_weeknum_iter.chain(neg_weeknum_iter);

write!(f, "[{}", weeknum_iter.next().unwrap())?;

for num in weeknum_iter {
Expand Down
60 changes: 58 additions & 2 deletions opening-hours-syntax/src/rules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ pub mod time;
use std::fmt::Display;
use std::sync::Arc;

use crate::rubik::Paving;
use crate::simplify::{canonical_to_seq, seq_to_canonical};
use crate::sorted_vec::UniqueSortedVec;

// OpeningHoursExpression
Expand All @@ -13,6 +15,46 @@ pub struct OpeningHoursExpression {
pub rules: Vec<RuleSequence>,
}

impl OpeningHoursExpression {
pub fn simplify(self) -> Self {
let mut rules_queue = self.rules.into_iter().peekable();
let mut simplified = Vec::new();

while let Some(head) = rules_queue.next() {
if head.operator != RuleOperator::Normal && head.operator != RuleOperator::Additional {
simplified.push(head);
break;
}

let Some(mut canonical) = seq_to_canonical(&head) else {
simplified.push(head);
break;
};

while rules_queue.peek().is_some()
&& rules_queue.peek().unwrap().operator == head.operator
&& rules_queue.peek().unwrap().kind == head.kind
&& rules_queue.peek().unwrap().comments == head.comments
&& seq_to_canonical(rules_queue.peek().unwrap()).is_some()
{
canonical.union_with(
// TODO: computed twice
seq_to_canonical(&rules_queue.next().unwrap()).unwrap(),
);
}

simplified.extend(canonical_to_seq(
canonical,
head.operator,
head.kind,
head.comments,
))
}

Self { rules: simplified }
}
}

impl Display for OpeningHoursExpression {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Some(first) = self.rules.first() else {
Expand Down Expand Up @@ -47,18 +89,32 @@ pub struct RuleSequence {
}

impl RuleSequence {
fn try_into_paving(self) {}
/// If this returns `true`, then this expression is always open, but it
/// can't detect all cases.
pub(crate) fn is_24_7(&self) -> bool {
self.day_selector.is_empty()
&& self.time_selector.is_00_24()
&& self.operator == RuleOperator::Normal
}
}

impl Display for RuleSequence {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.is_24_7() {
return write!(f, "24/7 {}", self.kind);
}

write!(f, "{}", self.day_selector)?;

if !self.day_selector.is_empty() && !self.time_selector.time.is_empty() {
write!(f, " ")?;
}

write!(f, "{} {}", self.time_selector, self.kind)
if !self.time_selector.is_00_24() {
write!(f, "{} ", self.time_selector)?;
}

write!(f, "{}", self.kind)
}
}

Expand Down
13 changes: 13 additions & 0 deletions opening-hours-syntax/src/rules/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,19 @@ pub struct TimeSelector {
pub time: Vec<TimeSpan>,
}

impl TimeSelector {
/// Note that not all cases can be covered
pub(crate) fn is_00_24(&self) -> bool {
self.time.len() == 1
&& matches!(
self.time.first(),
Some(TimeSpan { range, open_end: false, repeats: None })
if range.start == Time::Fixed(ExtendedTime::new(0,0).unwrap())
&& range.end == Time::Fixed( ExtendedTime::new(24,0).unwrap())
)
}
}

impl TimeSelector {
#[inline]
pub fn new(time: Vec<TimeSpan>) -> Self {
Expand Down
Loading

0 comments on commit 5fadabd

Please sign in to comment.