1use std::collections::HashMap;
8use std::error;
9use std::fmt::Write;
10use std::hash::{DefaultHasher, Hash, Hasher};
11use std::io::{BufRead, Lines};
12use std::rc::Rc;
13
14use tetengo_lattice::{Entry, HashMapVocabulary, StringInput, Vocabulary};
15
16#[derive(Debug, thiserror::Error)]
20pub(crate) enum TimetableError {
21 #[error("unexpected end of file")]
25 UnexpectedEndOfFile,
26
27 #[error("station names and telegram codes unmatch")]
31 StationNamesAndTelegramCodesUnmatch,
32
33 #[error("invalid train line found")]
37 InvalidTrainLineFound,
38
39 #[error("invalid arrival/departure time found")]
43 InvalidArrivalOrDepartureTimeFound,
44
45 #[error("invalid time found")]
49 InvalidTimeFound,
50
51 #[error("both arrival and departure time not found")]
55 BothArrivalAndDepartureTimeNotFound,
56
57 #[error("internal error: {0}")]
61 InternalError(#[from] Box<dyn error::Error>),
62}
63
64#[derive(Debug)]
68pub(crate) struct Station {
69 name: String,
70 telegram_code: String,
71}
72
73impl Station {
74 pub(crate) const fn new(name: String, telegram_code: String) -> Self {
82 Self {
83 name,
84 telegram_code,
85 }
86 }
87
88 pub(crate) const fn name(&self) -> &str {
95 self.name.as_str()
96 }
97
98 pub(crate) const fn telegram_code(&self) -> &str {
105 self.telegram_code.as_str()
106 }
107}
108
109#[derive(Clone, Debug)]
113pub(crate) struct Stop {
114 arrival_time: Option<usize>,
115 departure_time: Option<usize>,
116}
117
118impl Stop {
119 pub(crate) const fn new(arrival_time: Option<usize>, departure_time: Option<usize>) -> Self {
127 Self {
128 arrival_time,
129 departure_time,
130 }
131 }
132
133 pub(crate) const fn arrival_time(&self) -> Option<usize> {
140 self.arrival_time
141 }
142
143 pub(crate) const fn set_arrival_time(&mut self, time: usize) {
150 self.arrival_time = Some(time);
151 }
152
153 pub(crate) const fn departure_time(&self) -> Option<usize> {
160 self.departure_time
161 }
162
163 pub(crate) const fn set_departure_time(&mut self, time: usize) {
170 self.departure_time = Some(time);
171 }
172}
173
174#[derive(Clone, Debug)]
178pub(crate) struct Train {
179 number: String,
180 name: String,
181 stops: Vec<Stop>,
182}
183
184impl Train {
185 pub(crate) const fn new(number: String, name: String, stops: Vec<Stop>) -> Self {
194 Self {
195 number,
196 name,
197 stops,
198 }
199 }
200
201 pub(crate) const fn number(&self) -> &str {
208 self.number.as_str()
209 }
210
211 pub(crate) const fn name(&self) -> &str {
218 self.name.as_str()
219 }
220
221 pub(crate) const fn stops(&self) -> &[Stop] {
228 self.stops.as_slice()
229 }
230
231 pub(crate) const fn stops_mut(&mut self) -> &mut Vec<Stop> {
238 &mut self.stops
239 }
240}
241
242#[derive(Clone, Debug)]
246pub(crate) struct Section {
247 train: Rc<Train>,
248 from: usize,
249 to: usize,
250}
251
252impl Section {
253 pub(crate) const fn new(train: Rc<Train>, from: usize, to: usize) -> Self {
262 Self { train, from, to }
263 }
264
265 pub(crate) fn train(&self) -> &Train {
272 self.train.as_ref()
273 }
274
275 pub(crate) const fn from(&self) -> usize {
282 self.from
283 }
284
285 pub(crate) const fn to(&self) -> usize {
292 self.to
293 }
294}
295
296#[derive(Debug)]
297struct TimetableValue {
298 stations: Vec<Station>,
299 trains: Vec<Train>,
300}
301
302impl TimetableValue {
303 const fn new(stations: Vec<Station>, trains: Vec<Train>) -> Self {
304 Self { stations, trains }
305 }
306}
307
308#[derive(Debug)]
312pub(crate) struct Timetable {
313 value: TimetableValue,
314}
315
316impl Timetable {
317 pub(crate) fn new(reader: Box<dyn BufRead>) -> Result<Self, TimetableError> {
324 Ok(Self {
325 value: Self::build_timetable(reader)?,
326 })
327 }
328
329 fn build_timetable(mut reader: Box<dyn BufRead>) -> Result<TimetableValue, TimetableError> {
330 let mut value = Self::parse_input(reader.as_mut())?;
331 Self::guess_arrival_times(&mut value)?;
332 Ok(value)
333 }
334
335 fn parse_input(reader: &mut dyn BufRead) -> Result<TimetableValue, TimetableError> {
336 let mut lines = reader.lines();
337
338 let stations = {
339 let Some(line1) = Self::read_line(&mut lines)? else {
340 return Err(TimetableError::UnexpectedEndOfFile);
341 };
342 let Some(line2) = Self::read_line(&mut lines)? else {
343 return Err(TimetableError::UnexpectedEndOfFile);
344 };
345 Self::parse_stations(line1, line2)?
346 };
347
348 let trains = {
349 let mut trains = Vec::new();
350 while let Some(line) = Self::read_line(&mut lines)? {
351 if line.is_empty() || (line.len() == 1 && line[0].is_empty()) {
352 continue;
353 }
354 trains.push(Self::parse_train(line, stations.len())?);
355 }
356 trains
357 };
358
359 Ok(TimetableValue::new(stations, trains))
360 }
361
362 fn read_line(
363 lines: &mut Lines<&mut dyn BufRead>,
364 ) -> Result<Option<Vec<String>>, TimetableError> {
365 let Some(line) = lines.next() else {
366 return Ok(None);
367 };
368 let line = line.map_err(|e| TimetableError::InternalError(e.into()))?;
369 let elements = line
370 .split(',')
371 .map(|e| e.trim().to_string())
372 .collect::<Vec<_>>();
373 Ok(Some(elements))
374 }
375
376 fn parse_stations(
377 line1: Vec<String>,
378 line2: Vec<String>,
379 ) -> Result<Vec<Station>, TimetableError> {
380 if line1.len() != line2.len() {
381 return Err(TimetableError::StationNamesAndTelegramCodesUnmatch);
382 }
383 let stations = line1
384 .into_iter()
385 .skip(2)
386 .zip(line2.into_iter().skip(2))
387 .map(|(name, telegram_code)| Station::new(name, telegram_code))
388 .collect::<Vec<_>>();
389 Ok(stations)
390 }
391
392 fn parse_train(mut line: Vec<String>, station_count: usize) -> Result<Train, TimetableError> {
393 if line.len() > station_count + 2 {
394 return Err(TimetableError::InvalidTrainLineFound);
395 }
396 line.resize(station_count + 2, String::new());
397 let number = line[0].clone();
398 let name = line[1].clone();
399 let stops = line
400 .into_iter()
401 .skip(2)
402 .map(|s| Self::to_stop(&s))
403 .collect::<Result<Vec<_>, TimetableError>>()?;
404 Ok(Train::new(number, name, stops))
405 }
406
407 fn to_stop(element: &str) -> Result<Stop, TimetableError> {
408 let string_times = element
409 .split('/')
410 .map(|e| e.trim().to_string())
411 .collect::<Vec<_>>();
412 if string_times.is_empty() || string_times.len() > 2 {
413 Err(TimetableError::InvalidArrivalOrDepartureTimeFound)
414 } else if string_times.len() == 1 {
415 Ok(Stop::new(None, Self::to_minutes(string_times[0].as_str())?))
416 } else {
417 Ok(Stop::new(
418 Self::to_minutes(string_times[0].as_str())?,
419 Self::to_minutes(string_times[1].as_str())?,
420 ))
421 }
422 }
423
424 fn to_minutes(string_time: &str) -> Result<Option<usize>, TimetableError> {
425 if string_time.is_empty() || string_time == "-" {
426 return Ok(None);
427 }
428 let int_time = string_time
429 .parse::<usize>()
430 .map_err(|e| TimetableError::InternalError(e.into()))?;
431 let hour = int_time / 100;
432 let minute = int_time - hour * 100;
433 if hour >= 24 || minute >= 60 {
434 return Err(TimetableError::InvalidTimeFound);
435 }
436 Ok(Some(hour * 60 + minute))
437 }
438
439 fn guess_arrival_times(value: &mut TimetableValue) -> Result<(), TimetableError> {
440 for from in 0..value.stations.len() - 1 {
441 for to in from + 1..value.stations.len() {
442 let minimum_duration = Self::minimum_duration(value.trains.as_ref(), from, to)?;
443 for train in &mut value.trains {
444 if !Self::all_passing(train.stops(), from, to) {
445 continue;
446 }
447 if train.stops()[to].arrival_time().is_none() {
448 let Some(from_departure_time) = train.stops()[from].departure_time() else {
449 return Err(TimetableError::BothArrivalAndDepartureTimeNotFound);
450 };
451 train.stops_mut()[to].set_arrival_time(Self::add_time(
452 from_departure_time,
453 minimum_duration,
454 )?);
455 } else if train.stops()[from].departure_time().is_none() {
456 let Some(to_arrival_time) = train.stops()[to].arrival_time() else {
457 return Err(TimetableError::BothArrivalAndDepartureTimeNotFound);
458 };
459 train.stops_mut()[from].set_departure_time(Self::add_time(
460 to_arrival_time,
461 -minimum_duration,
462 )?);
463 }
464 }
465 }
466 }
467 Ok(())
468 }
469
470 fn minimum_duration(trains: &[Train], from: usize, to: usize) -> Result<isize, TimetableError> {
471 let mut minimum = isize::MAX;
472 for train in trains {
473 if !Self::all_passing(train.stops(), from, to) {
474 continue;
475 }
476 let from_time = if let Some(departure_time) = train.stops()[from].departure_time() {
477 departure_time
478 } else if let Some(arrival_time) = train.stops()[from].arrival_time() {
479 arrival_time
480 } else {
481 return Err(TimetableError::BothArrivalAndDepartureTimeNotFound);
482 };
483 let to_time = if let Some(arrival_time) = train.stops()[to].arrival_time() {
484 arrival_time
485 } else if let Some(departure_time) = train.stops()[to].departure_time() {
486 departure_time
487 } else {
488 return Err(TimetableError::BothArrivalAndDepartureTimeNotFound);
489 };
490 let duration = Self::diff_time(to_time, from_time)?;
491 if duration < minimum {
492 minimum = duration;
493 }
494 }
495 Ok(minimum)
496 }
497
498 pub(crate) const fn stations(&self) -> &[Station] {
505 self.value.stations.as_slice()
506 }
507
508 pub(crate) fn station_index(&self, name_or_telegram_code: &str) -> usize {
518 for (i, station) in self.value.stations.iter().enumerate() {
519 if station.name().to_lowercase() == name_or_telegram_code.to_lowercase()
520 || station.telegram_code().to_uppercase() == name_or_telegram_code.to_uppercase()
521 {
522 return i;
523 }
524 }
525 self.value.stations.len()
526 }
527
528 pub(crate) fn create_vocabulary(
538 &self,
539 departure_time: usize,
540 ) -> Result<Rc<dyn Vocabulary>, TimetableError> {
541 let entries = Self::build_entries(&self.value)?;
542 let connections = Self::build_connections(&entries, departure_time)?;
543 Ok(Rc::new(HashMapVocabulary::new(
544 entries,
545 connections,
546 &Self::entry_hash_value,
547 &Self::entry_equal_to,
548 )))
549 }
550
551 fn build_entries(
552 timetable: &TimetableValue,
553 ) -> Result<Vec<(String, Vec<Entry>)>, TimetableError> {
554 let mut map = HashMap::<String, Vec<Entry>>::new();
555 for train in &timetable.trains {
556 for from in 0..timetable.stations.len() - 1 {
557 for to in from + 1..timetable.stations.len() {
558 if !Self::all_passing(train.stops(), from, to) {
559 continue;
560 }
561
562 let section_name = Self::make_section_name(&timetable.stations, from, to);
563 let found = map.entry(section_name.clone()).or_default();
564 let section = Section::new(Rc::new(train.clone()), from, to);
565 let section_duration = Self::make_section_duration(train.stops(), from, to)?;
566 let cost = i32::try_from(section_duration)
567 .map_err(|e| TimetableError::InternalError(e.into()))?;
568 found.push(Entry::new(
569 Box::new(StringInput::new(section_name)),
570 Box::new(section),
571 cost,
572 ));
573 }
574 }
575 }
576 Ok(map.into_iter().collect::<Vec<_>>())
577 }
578
579 fn all_passing(stops: &[Stop], from: usize, to: usize) -> bool {
580 if stops[from].arrival_time().is_none() && stops[from].departure_time().is_none() {
581 return false;
582 }
583 if stops[to].arrival_time().is_none() && stops[to].departure_time().is_none() {
584 return false;
585 }
586 for stop in stops.iter().take(to).skip(from + 1) {
587 if stop.arrival_time().is_some() || stop.departure_time().is_some() {
588 return false;
589 }
590 }
591 true
592 }
593
594 fn make_section_name(stations: &[Station], from: usize, to: usize) -> String {
595 let mut name = String::new();
596 for i in from..to {
597 let _ = write!(
598 name,
599 "{}-{}/",
600 stations[i].telegram_code(),
601 stations[i + 1].telegram_code()
602 );
603 }
604 name
605 }
606
607 fn make_section_duration(
608 stops: &[Stop],
609 from: usize,
610 to: usize,
611 ) -> Result<usize, TimetableError> {
612 let departure_time = stops[from].departure_time().unwrap_or_else(|| {
613 unreachable!("departure_time must be set.");
614 });
615 let arrival_time = stops[to].arrival_time().unwrap_or_else(|| {
616 unreachable!("arrival_time must be set.");
617 });
618 let diff_result = Self::diff_time(arrival_time, departure_time)?;
619 usize::try_from(diff_result).map_err(|e| TimetableError::InternalError(e.into()))
620 }
621
622 fn build_connections(
623 entries: &[(String, Vec<Entry>)],
624 departure_time: usize,
625 ) -> Result<Vec<((Entry, Entry), i32)>, TimetableError> {
626 let mut connections = Vec::<((Entry, Entry), i32)>::new();
627
628 for (_, from_entries) in entries {
629 for (_, to_entries) in entries {
630 for from_entry in from_entries {
631 for to_entry in to_entries {
632 let from_value = from_entry
633 .value()
634 .unwrap_or_else(|| {
635 unreachable!("from_entry.value() must not be empty.")
636 })
637 .downcast_ref::<Section>()
638 .unwrap_or_else(|| unreachable!("from_entry.value() must be Section."));
639 let to_value = to_entry
640 .value()
641 .unwrap_or_else(|| unreachable!("to_entry.value() must not be empty."))
642 .downcast_ref::<Section>()
643 .unwrap_or_else(|| unreachable!("to_entry.value() must be Section."));
644 if from_value.to() != to_value.from() {
645 continue;
646 }
647
648 let from_arrival_time = from_value.train().stops()[from_value.to()]
649 .arrival_time()
650 .unwrap_or_else(|| {
651 unreachable!("from arrival_time must be set.");
652 });
653 let to_departure_time = to_value.train().stops()[to_value.from()]
654 .departure_time()
655 .unwrap_or_else(|| {
656 unreachable!("to departure_time must be set.");
657 });
658 let time_diff = Self::diff_time(to_departure_time, from_arrival_time)?;
659 let cost = i32::try_from(time_diff)
660 .map_err(|e| TimetableError::InternalError(e.into()))?;
661 if cost > 60 {
662 continue;
663 }
664 if from_value.train().number() == to_value.train().number() {
665 connections.push(((from_entry.clone(), to_entry.clone()), cost));
666 } else {
667 connections.push(((from_entry.clone(), to_entry.clone()), cost + 1));
668 }
669 }
670 }
671 }
672 }
673
674 for (_, entries) in entries {
675 for entry in entries {
676 let section = entry
677 .value()
678 .unwrap_or_else(|| unreachable!("entry.value() must not be empty."))
679 .downcast_ref::<Section>()
680 .unwrap_or_else(|| unreachable!("entry.value() must be Section."));
681 let section_departure_time = section.train().stops()[section.from()]
682 .departure_time()
683 .unwrap_or_else(|| {
684 unreachable!("departure_time() must be set.");
685 });
686 let time_diff = Self::diff_time(section_departure_time, departure_time)?;
687 let bos_cost = i32::try_from(time_diff)
688 .map_err(|e| TimetableError::InternalError(e.into()))?;
689 if bos_cost <= 240 {
690 connections.push(((Entry::BosEos, entry.clone()), bos_cost * 9 / 10));
691 }
692 connections.push(((entry.clone(), Entry::BosEos), 0));
693 }
694 }
695
696 Ok(connections)
697 }
698
699 fn add_time(time: usize, duration: isize) -> Result<usize, TimetableError> {
700 assert!(time < 1440);
701 assert!(-1440 < duration && duration < 1440);
702 let time_isize =
703 isize::try_from(time).map_err(|e| TimetableError::InternalError(e.into()))?;
704 let result = (time_isize + 1440 + duration) % 1440;
705 usize::try_from(result).map_err(|e| TimetableError::InternalError(e.into()))
706 }
707
708 fn diff_time(time1: usize, time2: usize) -> Result<isize, TimetableError> {
709 assert!(time1 < 1440);
710 assert!(time2 < 1440);
711 let time1_isize =
712 isize::try_from(time1).map_err(|e| TimetableError::InternalError(e.into()))?;
713 let time2_isize =
714 isize::try_from(time2).map_err(|e| TimetableError::InternalError(e.into()))?;
715 Ok((time1_isize + 1440 - time2_isize) % 1440)
716 }
717
718 fn entry_hash_value(entry: &Entry) -> u64 {
719 let mut hasher = DefaultHasher::new();
720
721 hasher.write_u64(entry.key().map_or(0, tetengo_lattice::Input::hash_value));
722 let section = entry
723 .value()
724 .and_then(|value| value.downcast_ref::<Section>());
725 if let Some(section) = section {
726 section.train().number().hash(&mut hasher);
727 section.train().name().hash(&mut hasher);
728 section.from().hash(&mut hasher);
729 section.to().hash(&mut hasher);
730 } else {
731 "".hash(&mut hasher);
732 "".hash(&mut hasher);
733 0usize.hash(&mut hasher);
734 0usize.hash(&mut hasher);
735 }
736 hasher.finish()
737 }
738
739 fn entry_equal_to(one: &Entry, another: &Entry) -> bool {
740 one.value().map_or_else(
741 || {
742 if another.value().is_none() {
743 one.key().map_or_else(
744 || another.key().is_none(),
745 |one_key| {
746 another
747 .key()
748 .is_some_and(|another_key| one_key.equal_to(another_key))
749 },
750 )
751 } else {
752 false
753 }
754 },
755 |one_value| {
756 another.value().is_some_and(|another_value| {
757 let Some(one_section) = one_value.downcast_ref::<Section>() else {
758 unreachable!("one.value() must be Section.");
759 };
760 let Some(another_section) = another_value.downcast_ref::<Section>() else {
761 unreachable!("another.value() must be Section.");
762 };
763 one.key().map_or_else(
764 || another.key().is_none(),
765 |one_key| {
766 another
767 .key()
768 .is_some_and(|another_key| one_key.equal_to(another_key))
769 },
770 ) && one_section.train().number() == another_section.train().number()
771 && one_section.train().name() == another_section.train().name()
772 && one_section.from() == another_section.from()
773 && one_section.to() == another_section.to()
774 })
775 },
776 )
777 }
778}