#[cfg(feature = "datagen")]
use crate::provider::packed_pattern::GenericUnpackedPatterns;
use crate::provider::packed_pattern::{GenericPackedPatterns, PackedPatternsBuilderHelper};
use crate::provider::pattern::runtime::PatternBorrowed;
use crate::provider::pattern::runtime::{Pattern, PatternULE};
use icu_provider::prelude::*;
use zerofrom::ZeroFrom;
use zerovec::VarZeroVec;
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[repr(u8)]
pub enum DateGreatestDifferenceField {
Day = 0,
Month = 1,
Year = 2,
Era = 3,
}
impl DateGreatestDifferenceField {
pub const MAX_VALUE: u8 = Self::Era as u8;
#[cfg(feature = "datagen")]
pub fn from_symbol(symbol: &str) -> Option<Self> {
match symbol {
"d" => Some(Self::Day),
"M" => Some(Self::Month),
"y" => Some(Self::Year),
"G" => Some(Self::Era),
_ => None,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[repr(u8)]
pub enum TimeGreatestDifferenceField {
Minute = 0,
Hour = 1,
DayPeriodB = 2,
DayPeriodA = 3,
}
impl TimeGreatestDifferenceField {
pub const MAX_VALUE: u8 = Self::DayPeriodA as u8;
#[cfg(feature = "datagen")]
pub fn from_symbol(symbol: &str) -> Option<Self> {
match symbol {
"m" => Some(Self::Minute),
"h" | "H" => Some(Self::Hour),
"B" => Some(Self::DayPeriodB),
"a" => Some(Self::DayPeriodA),
_ => None,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(u8)]
pub enum RangeStructure {
Absent = 0b00,
Symmetric = 0b01,
FullRange = 0b10,
}
impl RangeStructure {
pub const fn from_bits(bits: u8) -> Self {
match bits & 0b11 {
0b00 => Self::Absent,
0b01 => Self::Symmetric,
0b10 => Self::FullRange,
_ => Self::Absent, }
}
pub const fn num_patterns(self) -> usize {
match self {
Self::Absent => 0,
Self::Symmetric => 1,
Self::FullRange => 1,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RangePatternInfo<'a> {
Symmetric(Pattern<'a>),
FullRange(Pattern<'a>),
}
impl<'a> RangePatternInfo<'a> {
pub fn pattern(&self) -> &Pattern<'a> {
match self {
Self::Symmetric(p) => p,
Self::FullRange(p) => p,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum RangePatternInfoBorrowed<'a> {
Symmetric(PatternBorrowed<'a>),
FullRange(PatternBorrowed<'a>),
}
impl<'a> From<RangePatternInfoBorrowed<'a>> for RangePatternInfo<'a> {
fn from(other: RangePatternInfoBorrowed<'a>) -> Self {
match other {
RangePatternInfoBorrowed::Symmetric(p) => RangePatternInfo::Symmetric(p.as_pattern()),
RangePatternInfoBorrowed::FullRange(p) => RangePatternInfo::FullRange(p.as_pattern()),
}
}
}
impl<'a> ZeroFrom<'a, RangePatternInfoBorrowed<'a>> for RangePatternInfo<'a> {
fn zero_from(other: &'a RangePatternInfoBorrowed<'a>) -> Self {
Self::from(*other)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
#[zerovec::make_ule(GreatestDifferenceHeaderULE)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "datagen", derive(databake::Bake))]
#[cfg_attr(feature = "datagen", databake(path = icu_datetime::provider::range_patterns))]
#[zerovec::skip_derive(Ord)]
pub struct GreatestDifferenceHeader(pub u8);
impl GreatestDifferenceHeader {
pub const fn new(val: u8) -> Self {
Self(val)
}
pub const fn get_state(self, field_idx: u8) -> RangeStructure {
let shift = field_idx * 2;
let bits = (self.0 >> shift) & 0b11;
RangeStructure::from_bits(bits)
}
#[cfg(any(feature = "datagen", test))]
pub fn set_state(&mut self, field_idx: u8, state: RangeStructure) {
let shift = field_idx * 2;
let mask = !(0b11 << shift);
self.0 = (self.0 & mask) | ((state as u8) << shift);
}
pub const fn is_date_field_present(self, field: DateGreatestDifferenceField) -> bool {
!matches!(self.get_state(field as u8), RangeStructure::Absent)
}
pub const fn is_time_field_present(self, field: TimeGreatestDifferenceField) -> bool {
!matches!(self.get_state(field as u8), RangeStructure::Absent)
}
pub const fn get_pattern_index(self, field_idx: u8) -> usize {
let mask = (1u16 << (field_idx * 2)) - 1;
let masked = (self.0 as u16) & mask;
masked.count_ones() as usize
}
pub const fn total_patterns(self) -> usize {
self.get_pattern_index(4)
}
}
#[derive(Debug, PartialEq, Eq, Clone, yoke::Yokeable, ZeroFrom, Default)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
#[cfg_attr(feature = "datagen", derive(serde::Serialize))]
#[cfg_attr(feature = "datagen", derive(databake::Bake))]
#[cfg_attr(feature = "datagen", databake(path = icu_datetime::provider::range_patterns))]
#[zerovec::make_varule(PatternsByGreatestDifferenceULE)]
#[zerovec::derive(Debug)]
#[zerovec::skip_derive(Ord)]
#[cfg_attr(feature = "serde", zerovec::derive(Deserialize))]
#[cfg_attr(feature = "datagen", zerovec::derive(Serialize))]
pub struct PatternsByGreatestDifference<'data> {
pub header: GreatestDifferenceHeader,
#[cfg_attr(feature = "serde", serde(borrow))]
pub patterns: VarZeroVec<'data, PatternULE>,
}
fn resolve_fallback(header: GreatestDifferenceHeader, requested: u8, max_value: u8) -> Option<u8> {
for i in requested..=max_value {
match header.get_state(i) {
RangeStructure::Absent => {}
_ => return Some(i),
}
}
None
}
impl PatternsByGreatestDifferenceULE {
fn get_pattern_internal<'a>(&'a self, resolved_field_idx: u8) -> RangePatternInfoBorrowed<'a> {
use zerovec::ule::AsULE;
let header = <GreatestDifferenceHeader as AsULE>::from_unaligned(self.header);
let state = header.get_state(resolved_field_idx);
let start_idx = header.get_pattern_index(resolved_field_idx);
let get_pat = |offset| {
self.patterns
.get(start_idx + offset)
.map(<PatternBorrowed as ZeroFrom<PatternULE>>::zero_from)
.unwrap_or(PatternBorrowed::DEFAULT)
};
match state {
RangeStructure::Absent => RangePatternInfoBorrowed::Symmetric(PatternBorrowed::DEFAULT),
RangeStructure::Symmetric => RangePatternInfoBorrowed::Symmetric(get_pat(0)),
RangeStructure::FullRange => RangePatternInfoBorrowed::FullRange(get_pat(0)),
}
}
pub fn get_date_pattern<'a>(
&'a self,
field: DateGreatestDifferenceField,
) -> Option<RangePatternInfoBorrowed<'a>> {
use zerovec::ule::AsULE;
let header = <GreatestDifferenceHeader as AsULE>::from_unaligned(self.header);
let resolved_field_idx =
resolve_fallback(header, field as u8, DateGreatestDifferenceField::MAX_VALUE)?;
Some(self.get_pattern_internal(resolved_field_idx))
}
pub fn get_time_pattern<'a>(
&'a self,
field: TimeGreatestDifferenceField,
) -> Option<RangePatternInfoBorrowed<'a>> {
use zerovec::ule::AsULE;
let header = <GreatestDifferenceHeader as AsULE>::from_unaligned(self.header);
let resolved_field_idx =
resolve_fallback(header, field as u8, TimeGreatestDifferenceField::MAX_VALUE)?;
Some(self.get_pattern_internal(resolved_field_idx))
}
}
impl<'data> PatternsByGreatestDifference<'data> {
#[cfg(any(feature = "datagen", test))]
pub fn try_from_patterns<I>(iter: I) -> Result<Self, &'static str>
where
I: IntoIterator<Item = (u8, RangePatternInfo<'data>)>,
{
use alloc::vec::Vec;
let mut original_input = iter.into_iter().collect::<Vec<_>>();
original_input.sort_by_key(|(bit, _)| *bit);
if original_input.is_empty() {
return Err("PatternsByGreatestDifference cannot be empty");
}
let mut prev_bit = None;
for &(bit, _) in original_input.iter() {
if bit > 3 {
return Err("GreatestDifference bit index must be 0, 1, 2, or 3");
}
if prev_bit == Some(bit) {
return Err("Duplicate greatest difference fields are not allowed");
}
prev_bit = Some(bit);
}
let mut flat_patterns = Vec::new();
let mut header = GreatestDifferenceHeader::new(0);
let mut it = original_input.into_iter().peekable();
while let Some((bit, info)) = it.next() {
let keep = if let Some((_, next_info)) = it.peek() {
&info != next_info
} else {
true };
if keep {
let state = match info {
RangePatternInfo::Symmetric(pat) => {
flat_patterns.push(pat);
RangeStructure::Symmetric
}
RangePatternInfo::FullRange(pat) => {
flat_patterns.push(pat);
RangeStructure::FullRange
}
};
header.set_state(bit, state);
}
}
let varzerovec = VarZeroVec::from(flat_patterns.as_slice());
Ok(Self {
header,
patterns: varzerovec,
})
}
#[cfg(any(feature = "datagen", test))]
pub fn try_from_date_patterns<I>(iter: I) -> Result<Self, &'static str>
where
I: IntoIterator<Item = (DateGreatestDifferenceField, RangePatternInfo<'data>)>,
{
Self::try_from_patterns(iter.into_iter().map(|(f, p)| (f as u8, p)))
}
#[cfg(any(feature = "datagen", test))]
pub fn try_from_time_patterns<I>(iter: I) -> Result<Self, &'static str>
where
I: IntoIterator<Item = (TimeGreatestDifferenceField, RangePatternInfo<'data>)>,
{
Self::try_from_patterns(iter.into_iter().map(|(f, p)| (f as u8, p)))
}
}
impl PackedPatternsBuilderHelper for PatternsByGreatestDifferenceULE {
type Unpacked<'a> = PatternsByGreatestDifference<'a>;
fn pack(elements: &[Self::Unpacked<'_>]) -> VarZeroVec<'static, Self> {
VarZeroVec::from(elements)
}
#[cfg(feature = "datagen")]
fn unpack<'a>(
packed: &'a GenericPackedPatterns<Self>,
) -> GenericUnpackedPatterns<Self::Unpacked<'a>> {
use crate::provider::packed_pattern::constants;
let variant_indices = packed.variant_indices();
let elements = packed
.elements
.iter()
.map(|pgd_ule| {
<PatternsByGreatestDifference<'a> as ZeroFrom<
PatternsByGreatestDifferenceULE,
>>::zero_from(pgd_ule)
})
.collect();
GenericUnpackedPatterns {
has_explicit_medium: (packed.header & constants::M_DIFFERS) != 0,
has_explicit_short: (packed.header & constants::S_DIFFERS) != 0,
variant_indices,
elements,
}
}
}
#[cfg(feature = "datagen")]
impl<'a>
crate::provider::packed_pattern::GenericPackedPatternsBuilder<PatternsByGreatestDifference<'a>>
{
pub fn build(self) -> PackedRangePatterns<'static> {
let mut builder = self;
let unpacked = builder.build_unpacked();
unpacked.build::<PatternsByGreatestDifferenceULE>()
}
}
impl<'data> GenericPackedPatterns<'data, PatternsByGreatestDifferenceULE> {
#[cfg(feature = "unstable")]
pub(crate) fn get_coarse_hour_cycle(
&self,
length: crate::options::Length,
) -> Option<crate::provider::pattern::CoarseHourCycle> {
use crate::provider::packed_pattern::PackedSkeletonVariant;
let element = self.get_element(length, PackedSkeletonVariant::Standard)?;
let pattern_ule = element.patterns.get(0)?;
let pattern = <PatternBorrowed as ZeroFrom<PatternULE>>::zero_from(pattern_ule);
pattern.metadata.time_granularity().coarse_hour_cycle()
}
}
pub type PackedRangePatterns<'data> = GenericPackedPatterns<'data, PatternsByGreatestDifferenceULE>;
icu_provider::data_struct!(
PackedRangePatterns<'_>,
#[cfg(feature = "datagen")]
);
pub(crate) type ErasedPackedRangePatterns =
icu_provider::marker::ErasedMarker<PackedRangePatterns<'static>>;
icu_provider::data_marker!(
DatetimePatternsRangeGlueV1,
crate::provider::semantic_skeletons::GluePattern<'static>
);
icu_provider::data_marker!(
DatetimePatternsRangeTimeV1,
PackedRangePatterns<'static>
);
icu_provider::data_marker!(
DatetimePatternsRangeDateBuddhistV1,
PackedRangePatterns<'static>
);
icu_provider::data_marker!(
DatetimePatternsRangeDateChineseV1,
PackedRangePatterns<'static>
);
icu_provider::data_marker!(
DatetimePatternsRangeDateCopticV1,
PackedRangePatterns<'static>
);
icu_provider::data_marker!(
DatetimePatternsRangeDateDangiV1,
PackedRangePatterns<'static>
);
icu_provider::data_marker!(
DatetimePatternsRangeDateEthiopianV1,
PackedRangePatterns<'static>
);
icu_provider::data_marker!(
DatetimePatternsRangeDateGregorianV1,
PackedRangePatterns<'static>
);
icu_provider::data_marker!(
DatetimePatternsRangeDateHebrewV1,
PackedRangePatterns<'static>
);
icu_provider::data_marker!(
DatetimePatternsRangeDateIndianV1,
PackedRangePatterns<'static>
);
icu_provider::data_marker!(
DatetimePatternsRangeDateHijriV1,
PackedRangePatterns<'static>
);
icu_provider::data_marker!(
DatetimePatternsRangeDateJapaneseV1,
PackedRangePatterns<'static>
);
icu_provider::data_marker!(
DatetimePatternsRangeDatePersianV1,
PackedRangePatterns<'static>
);
icu_provider::data_marker!(
DatetimePatternsRangeDateRocV1,
PackedRangePatterns<'static>
);
#[cfg(test)]
mod tests {
use super::*;
use crate::provider::pattern::reference;
#[test]
fn test_get_pattern() {
let pattern1 = "d"
.parse::<reference::Pattern>()
.unwrap()
.to_runtime_pattern();
let pattern2 = "y"
.parse::<reference::Pattern>()
.unwrap()
.to_runtime_pattern();
let patterns = vec![pattern1.clone(), pattern2.clone()];
let varzerovec = VarZeroVec::from(patterns.as_slice());
let header = GreatestDifferenceHeader(17);
let pgd = PatternsByGreatestDifference {
header,
patterns: varzerovec,
};
assert!(
pgd.header
.is_date_field_present(DateGreatestDifferenceField::Day)
);
assert!(
!pgd.header
.is_date_field_present(DateGreatestDifferenceField::Month)
);
assert!(
pgd.header
.is_date_field_present(DateGreatestDifferenceField::Year)
);
let pgd_ule_box = zerovec::ule::encode_varule_to_box(&pgd);
let pgd: &PatternsByGreatestDifferenceULE = &pgd_ule_box;
let info_a = RangePatternInfo::Symmetric(pattern1);
let info_b = RangePatternInfo::Symmetric(pattern2);
assert_eq!(
pgd.get_date_pattern(DateGreatestDifferenceField::Day)
.map(RangePatternInfo::from)
.as_ref(),
Some(&info_a)
);
assert_eq!(
pgd.get_date_pattern(DateGreatestDifferenceField::Month)
.map(RangePatternInfo::from)
.as_ref(),
Some(&info_b)
);
assert_eq!(
pgd.get_date_pattern(DateGreatestDifferenceField::Year)
.map(RangePatternInfo::from)
.as_ref(),
Some(&info_b)
);
assert_eq!(
pgd.get_date_pattern(DateGreatestDifferenceField::Era)
.as_ref(),
None
);
}
#[test]
#[cfg(feature = "datagen")]
fn test_try_from_patterns() {
let pattern_d = "d"
.parse::<reference::Pattern>()
.unwrap()
.to_runtime_pattern();
let pattern_y = "y"
.parse::<reference::Pattern>()
.unwrap()
.to_runtime_pattern();
let info_d = RangePatternInfo::Symmetric(pattern_d);
let info_y = RangePatternInfo::Symmetric(pattern_y);
let pgd = PatternsByGreatestDifference::try_from_date_patterns(
alloc::collections::BTreeMap::from([
(DateGreatestDifferenceField::Day, info_d.clone()),
(DateGreatestDifferenceField::Year, info_y.clone()),
]),
)
.unwrap();
assert_eq!(pgd.header.0, 17);
assert_eq!(pgd.patterns.len(), 2);
assert_eq!(
pgd.patterns
.get(0)
.map(<Pattern as ZeroFrom<PatternULE>>::zero_from)
.as_ref(),
Some(info_d.pattern())
);
assert_eq!(
pgd.patterns
.get(1)
.map(<Pattern as ZeroFrom<PatternULE>>::zero_from)
.as_ref(),
Some(info_y.pattern())
);
let pgd2 = PatternsByGreatestDifference::try_from_date_patterns(
alloc::collections::BTreeMap::from([
(DateGreatestDifferenceField::Year, info_y),
(DateGreatestDifferenceField::Day, info_d.clone()),
]),
)
.unwrap();
assert_eq!(pgd2.header.0, 17);
assert_eq!(
pgd2.patterns
.get(0)
.map(<Pattern as ZeroFrom<PatternULE>>::zero_from)
.as_ref(),
Some(info_d.pattern())
);
let err = PatternsByGreatestDifference::try_from_date_patterns(
alloc::collections::BTreeMap::new(),
);
assert!(err.is_err());
}
}
#[cfg(feature = "serde")]
mod _serde {
use super::*;
use crate::provider::packed_pattern::_serde::PackedPatternsSerdeHelper;
use alloc::vec::Vec;
use serde::Deserialize;
#[cfg(feature = "datagen")]
use serde::Serialize;
#[derive(Debug, Clone, Default, Deserialize)]
#[cfg_attr(feature = "datagen", derive(Serialize))]
pub struct PatternsByGreatestDifferenceHuman {
pub header: GreatestDifferenceHeader,
pub patterns: Vec<crate::provider::pattern::reference::Pattern>,
}
impl PackedPatternsSerdeHelper for PatternsByGreatestDifferenceULE {
type Human = PatternsByGreatestDifferenceHuman;
fn human_to_unpacked_element<'a>(human: &'a Self::Human) -> Self::Unpacked<'a> {
let patterns: Vec<Pattern<'a>> = human
.patterns
.iter()
.map(|p| p.to_runtime_pattern())
.collect();
PatternsByGreatestDifference {
header: human.header,
patterns: VarZeroVec::from(&patterns),
}
}
#[cfg(feature = "datagen")]
fn unpacked_element_to_human<S: serde::Serializer>(
element: Self::Unpacked<'_>,
) -> Result<Self::Human, S::Error> {
let patterns = element
.patterns
.iter()
.map(|p| {
let runtime_pattern = <Pattern as ZeroFrom<PatternULE>>::zero_from(p);
crate::provider::pattern::reference::Pattern::from(&runtime_pattern)
})
.collect();
Ok(PatternsByGreatestDifferenceHuman {
header: element.header,
patterns,
})
}
}
impl<'de, 'data> Deserialize<'de> for PackedRangePatterns<'data>
where
'de: 'data,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
GenericPackedPatterns::deserialize_impl(deserializer)
}
}
#[cfg(feature = "datagen")]
impl Serialize for PackedRangePatterns<'_> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.serialize_impl(serializer)
}
}
}
#[cfg(test)]
mod try_from_patterns_tests {
use super::*;
use crate::provider::pattern::runtime::Pattern;
use core::str::FromStr;
#[test]
fn test_try_from_patterns_dedup() {
let pat_a = Pattern::from_str("y").unwrap();
let pat_b = Pattern::from_str("m").unwrap();
let info_a = RangePatternInfo::Symmetric(pat_a.clone());
let info_b = RangePatternInfo::Symmetric(pat_b);
let info_c = RangePatternInfo::FullRange(pat_a);
let input = vec![
(0, info_a.clone()),
(1, info_b.clone()),
(2, info_c.clone()),
];
let pgd = PatternsByGreatestDifference::try_from_patterns(input).unwrap();
assert_eq!(pgd.header.0, 0x25);
assert_eq!(pgd.patterns.len(), 3);
let pgd_ule_box = zerovec::ule::encode_varule_to_box(&pgd);
let pgd: &PatternsByGreatestDifferenceULE = &pgd_ule_box;
assert_eq!(
pgd.get_date_pattern(DateGreatestDifferenceField::Day)
.map(RangePatternInfo::from)
.as_ref(),
Some(&info_a)
);
assert_eq!(
pgd.get_date_pattern(DateGreatestDifferenceField::Month)
.map(RangePatternInfo::from)
.as_ref(),
Some(&info_b)
);
assert_eq!(
pgd.get_date_pattern(DateGreatestDifferenceField::Year)
.map(RangePatternInfo::from)
.as_ref(),
Some(&info_c)
);
let input = vec![
(0, info_a.clone()),
(1, info_a.clone()),
(2, info_a.clone()),
];
let pgd = PatternsByGreatestDifference::try_from_patterns(input).unwrap();
assert_eq!(pgd.header.0, 0x10);
assert_eq!(pgd.patterns.len(), 1);
let pgd_ule_box = zerovec::ule::encode_varule_to_box(&pgd);
let pgd: &PatternsByGreatestDifferenceULE = &pgd_ule_box;
assert_eq!(
pgd.get_date_pattern(DateGreatestDifferenceField::Day)
.map(RangePatternInfo::from)
.as_ref(),
Some(&info_a)
);
assert_eq!(
pgd.get_date_pattern(DateGreatestDifferenceField::Month)
.map(RangePatternInfo::from)
.as_ref(),
Some(&info_a)
);
assert_eq!(
pgd.get_date_pattern(DateGreatestDifferenceField::Year)
.map(RangePatternInfo::from)
.as_ref(),
Some(&info_a)
);
let input = vec![
(0, info_a.clone()),
(1, info_a.clone()),
(2, info_b.clone()),
];
let pgd = PatternsByGreatestDifference::try_from_patterns(input).unwrap();
assert_eq!(pgd.header.0, 0x14);
assert_eq!(pgd.patterns.len(), 2);
let pgd_ule_box = zerovec::ule::encode_varule_to_box(&pgd);
let pgd: &PatternsByGreatestDifferenceULE = &pgd_ule_box;
assert_eq!(
pgd.get_date_pattern(DateGreatestDifferenceField::Day)
.map(RangePatternInfo::from)
.as_ref(),
Some(&info_a)
);
assert_eq!(
pgd.get_date_pattern(DateGreatestDifferenceField::Month)
.map(RangePatternInfo::from)
.as_ref(),
Some(&info_a)
);
assert_eq!(
pgd.get_date_pattern(DateGreatestDifferenceField::Year)
.map(RangePatternInfo::from)
.as_ref(),
Some(&info_b)
);
let input = vec![
(0, info_a.clone()),
(1, info_b.clone()),
(2, info_a.clone()),
];
let pgd = PatternsByGreatestDifference::try_from_patterns(input).unwrap();
assert_eq!(pgd.header.0, 0x15);
assert_eq!(pgd.patterns.len(), 3);
let input = vec![
(2, info_a.clone()),
(0, info_a.clone()),
(1, info_a.clone()),
];
let pgd = PatternsByGreatestDifference::try_from_patterns(input).unwrap();
assert_eq!(pgd.header.0, 0x10); assert_eq!(pgd.patterns.len(), 1);
let input = vec![(0, info_a.clone()), (0, info_b)];
assert!(PatternsByGreatestDifference::try_from_patterns(input).is_err());
let input = vec![(4, info_a)];
assert!(PatternsByGreatestDifference::try_from_patterns(input).is_err());
}
}