Initial commit

add officer
This commit is contained in:
Kristofers Solo 2024-03-17 16:08:53 +02:00
commit a8311c5e0f
29 changed files with 4640 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

72
Cargo.lock generated Normal file
View File

@ -0,0 +1,72 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "anyhow"
version = "1.0.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
[[package]]
name = "customs-rs"
version = "0.1.0"
dependencies = [
"anyhow",
"thiserror",
]
[[package]]
name = "proc-macro2"
version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
[[package]]
name = "syn"
version = "2.0.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "1.0.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"

13
Cargo.toml Normal file
View File

@ -0,0 +1,13 @@
[package]
name = "customs-rs"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
thiserror = "1"
anyhow = "1"
[dev-dependencies]

BIN
Muita.pdf Normal file

Binary file not shown.

2
data/input/customs.i1 Normal file
View File

@ -0,0 +1,2 @@
1 1 10 10
X

4
data/input/customs.i2 Normal file
View File

@ -0,0 +1,4 @@
1 1 10 10
P 1
N 2
X

4
data/input/customs.i3 Normal file
View File

@ -0,0 +1,4 @@
1 1 10 10
P 1
P 2
X

13
data/input/customs.i4 Normal file
View File

@ -0,0 +1,13 @@
2 1 10 10
T P 2 21
P 1
P 2
P 3
P 4
P 5
P 6
P 7
P 8
P 9
P 10
X

13
data/input/customs.i5 Normal file
View File

@ -0,0 +1,13 @@
1 5 10 10
T N 1 5
T N 3 3
T N 5 1
T N 4 2
T N 2 5
N 997
N 1001
N 1002
N 1003
N 1004
N 1005
X

14
data/input/customs.i6 Normal file
View File

@ -0,0 +1,14 @@
2 3 10 50
T P 1 7
T N 2 80
P 1
N 2
N 10
N 20
N 30
N 40
P 45
P 50
P 53
N 60
X

2004
data/input/customs.i7 Normal file

File diff suppressed because it is too large Load Diff

1
data/output/customs.o1 Normal file
View File

@ -0,0 +1 @@
nothing

2
data/output/customs.o2 Normal file
View File

@ -0,0 +1,2 @@
1 11
2 12

2
data/output/customs.o3 Normal file
View File

@ -0,0 +1,2 @@
1 11
2 21

10
data/output/customs.o4 Normal file
View File

@ -0,0 +1,10 @@
1 11
3 21
2 23
4 31
6 41
5 44
7 51
9 61
8 65
10 71

6
data/output/customs.o5 Normal file
View File

@ -0,0 +1,6 @@
997 1002
1001 1006
1003 1006
1004 1006
1005 1006
1002 1007

10
data/output/customs.o6 Normal file
View File

@ -0,0 +1,10 @@
1 8
45 52
2 52
53 60
50 60
20 70
10 90
30 102
40 120
60 170

2000
data/output/customs.o7 Normal file

File diff suppressed because it is too large Load Diff

48
src/citizenship.rs Normal file
View File

@ -0,0 +1,48 @@
use std::str::FromStr;
use crate::{error::InputError, utils::Time};
#[derive(Debug, PartialEq, Eq)]
pub enum CitizenshipType {
Citizen,
NonCitizen,
}
impl FromStr for CitizenshipType {
type Err = InputError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.trim() {
"P" => Ok(Self::Citizen),
"N" => Ok(Self::NonCitizen),
_ => Err(InputError::InvalidType("citizenship type".into())),
}
}
}
#[derive(Debug)]
pub struct Citizenship {
pub id: Time,
pub type_: CitizenshipType,
}
impl Citizenship {
pub fn new(id: Time, type_: CitizenshipType) -> Self {
Self { id, type_ }
}
}
impl FromStr for Citizenship {
type Err = InputError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut iter = s.split_whitespace();
match (iter.next(), iter.next()) {
(Some(citizenship), Some(id)) => {
let id = id.parse::<Time>().map_err(|_| InputError::InvalidFormat)?;
let type_ = CitizenshipType::from_str(citizenship)?;
Ok(Self { id, type_ })
}
_ => Err(InputError::InvalidFormat),
}
}
}

30
src/customs.rs Normal file
View File

@ -0,0 +1,30 @@
use crate::{citizenship::CitizenshipType, officer::Officer};
#[derive(Debug)]
pub struct Customs {
pub citizens: Vec<Officer>,
pub noncitizens: Vec<Officer>,
}
impl Customs {
pub fn new() -> Self {
Self {
citizens: Vec::new(),
noncitizens: Vec::new(),
}
}
pub fn push(&mut self, officer: Officer) {
match officer.citizenship {
CitizenshipType::Citizen => self.citizens.push(officer),
CitizenshipType::NonCitizen => self.noncitizens.push(officer),
}
}
pub fn is_empty(&self) -> bool {
self.citizens.is_empty() && self.noncitizens.is_empty()
}
pub fn sort(&mut self) {
self.citizens.sort();
self.noncitizens.sort();
}
}

66
src/customs_info.rs Normal file
View File

@ -0,0 +1,66 @@
use std::str::FromStr;
use crate::{
citizenship::CitizenshipType,
error::InputError,
utils::{CustomsCount, Time},
};
#[derive(Debug)]
pub struct DefaultOfficer {
pub count: CustomsCount,
pub time: Time,
pub citizenship: CitizenshipType,
}
impl DefaultOfficer {
fn new(count: CustomsCount, time: Time, citizenship: CitizenshipType) -> Self {
Self {
count,
time,
citizenship,
}
}
}
#[derive(Debug)]
pub struct CustomsInfo {
pub citizen: DefaultOfficer,
pub noncitizen: DefaultOfficer,
}
impl CustomsInfo {
pub fn count(&self) -> u8 {
self.citizen.count + self.noncitizen.count
}
}
impl FromStr for CustomsInfo {
type Err = InputError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let nums = s
.split_whitespace()
.map(|x| x.parse::<Time>().map_err(|_| InputError::InvalidFormat))
.collect::<Result<Vec<_>, _>>();
let nums = nums?;
let (citizen_customs, noncitizen_customs, citizen_time, noncitizen_time) = (
nums[0] as CustomsCount,
nums[1] as CustomsCount,
nums[2],
nums[3],
);
let citizen = DefaultOfficer::new(citizen_customs, citizen_time, CitizenshipType::Citizen);
let noncitizen = DefaultOfficer::new(
noncitizen_customs,
noncitizen_time,
CitizenshipType::NonCitizen,
);
Ok(Self {
citizen,
noncitizen,
})
}
}

24
src/error.rs Normal file
View File

@ -0,0 +1,24 @@
use thiserror::Error;
#[derive(Error, Debug)]
pub enum InputError {
#[error("invalid input format")]
InvalidFormat,
#[error("invalid {0}")]
InvalidType(String),
#[error("unknown input error")]
Unknown,
}
#[derive(Error, Debug)]
pub enum ParseError {
#[error("no first line found")]
NoFirstLine,
#[error("newcomer error")]
Newcomer,
#[error("citizenship error")]
Citizenship,
#[error("no citizens found")]
NoCitizens,
#[error("no customs found")]
NoCustoms,
}

8
src/lib.rs Normal file
View File

@ -0,0 +1,8 @@
pub mod citizenship;
pub mod customs;
pub mod customs_info;
pub mod error;
pub mod officer;
pub mod output;
pub mod process;
pub mod utils;

15
src/main.rs Normal file
View File

@ -0,0 +1,15 @@
use std::{fs::File, io::Read};
use anyhow::Result;
use customs_rs::process::process;
fn main() -> Result<()> {
let filename = "customs.i4";
println!("\nFilename: {}\n", &filename);
let mut file = File::open(format!("data/input/{}", &filename))?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
let result = process(&contents);
print!("{result}");
Ok(())
}

61
src/officer.rs Normal file
View File

@ -0,0 +1,61 @@
use std::str::FromStr;
use crate::{
citizenship::CitizenshipType,
error::InputError,
utils::{CustomsCount, Time},
};
#[derive(Debug, PartialEq, Eq)]
pub struct Officer {
pub id: CustomsCount,
pub time: Time,
pub citizenship: CitizenshipType,
}
impl Officer {
pub fn new(id: CustomsCount, time: Time, citizenship: CitizenshipType) -> Self {
Self {
id,
time,
citizenship,
}
}
}
impl PartialOrd for Officer {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.id.partial_cmp(&other.id)
}
}
impl Ord for Officer {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.id.cmp(&other.id)
}
}
impl FromStr for Officer {
type Err = InputError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut iter = s.split_whitespace();
match (iter.next(), iter.next(), iter.next(), iter.next()) {
(Some("T"), Some(citizenship_type), Some(customs_num), Some(time)) => {
let citizenship = CitizenshipType::from_str(&citizenship_type)?;
let time = time
.parse::<Time>()
.map_err(|_| InputError::InvalidFormat)?;
let id = customs_num
.parse::<CustomsCount>()
.map_err(|_| InputError::InvalidFormat)?;
Ok(Self {
id,
citizenship,
time,
})
}
_ => Err(InputError::InvalidFormat),
}
}
}

82
src/output.rs Normal file
View File

@ -0,0 +1,82 @@
use std::{cmp, fmt::Display};
use crate::{
citizenship::{Citizenship, CitizenshipType},
customs::Customs,
officer::Officer,
utils::Time,
};
#[derive(Debug)]
pub struct Output(Vec<OutputTime>);
#[derive(Debug)]
pub struct OutputTime {
input: Time,
output: Time,
}
impl Output {
pub fn new(mut customs: Customs, all_citizens: Vec<Citizenship>) -> Self {
customs.sort();
dbg!(&customs);
let citizens = Self::from((&customs.citizens, &all_citizens, CitizenshipType::Citizen));
let noncitizens = Self::from((
&customs.noncitizens,
&all_citizens,
CitizenshipType::NonCitizen,
));
let mut output = Vec::new();
output.extend(citizens.0);
output.extend(noncitizens.0);
Self(output)
}
}
impl From<(&Vec<Officer>, &Vec<Citizenship>, CitizenshipType)> for Output {
fn from(value: (&Vec<Officer>, &Vec<Citizenship>, CitizenshipType)) -> Self {
let (customs, citizens, type_) = value;
let mut outputs = Vec::new();
for officer in customs {
let mut prev = 0;
for citizen in citizens {
if citizen.type_ == officer.citizenship && citizen.type_ == type_ {
let output_ = officer.time + cmp::max(citizen.id, prev);
let output = OutputTime::new(citizen.id, output_);
outputs.push(output);
prev = output_;
}
}
}
Self(outputs)
}
}
impl Display for Output {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut output_string = String::new();
for time in &self.0 {
let output_str = format!("{}\n", time);
output_string.push_str(&output_str);
}
write!(f, "{}", output_string)
}
}
impl Into<String> for Output {
fn into(self) -> String {
self.to_string()
}
}
impl OutputTime {
pub fn new(input: Time, output: Time) -> Self {
Self { input, output }
}
}
impl Display for OutputTime {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} {}", self.input, self.output)
}
}

86
src/process.rs Normal file
View File

@ -0,0 +1,86 @@
use std::str::FromStr;
use crate::{
citizenship::{Citizenship, CitizenshipType},
customs::Customs,
customs_info::CustomsInfo,
error::ParseError,
officer::Officer,
output::Output,
};
pub fn process(input: &str) -> String {
match parse_input(input) {
Ok(data) => data.into(),
Err(e) => {
println!("Error: {e}");
"nothing\n".into()
}
}
}
pub fn parse_input(input: &str) -> Result<Output, ParseError> {
let mut lines = input.lines();
let first = match CustomsInfo::from_str(lines.next().unwrap()) {
Ok(info) => info,
Err(_) => return Err(ParseError::NoFirstLine),
};
let mut customs = Customs::new();
let mut citizens = Vec::new();
customs.citizens.reserve_exact(first.citizen.count as usize);
customs
.noncitizens
.reserve_exact(first.noncitizen.count as usize);
while let Some(line) = lines.next() {
if line.contains("X") {
break;
};
if let Ok(officer) = Officer::from_str(line) {
customs.push(officer);
}
if let Ok(citizen) = Citizenship::from_str(line) {
citizens.push(citizen);
}
}
if citizens.is_empty() {
return Err(ParseError::NoCitizens);
}
let mut citizen_customs_new = Vec::new();
for customs_id in 1..=first.citizen.count {
if customs
.citizens
.iter()
.any(|x| x.id == customs_id && x.citizenship == CitizenshipType::Citizen)
{
continue;
}
let new = Officer::new(customs_id, first.citizen.time, CitizenshipType::Citizen);
citizen_customs_new.push(new);
}
let mut noncitizen_customs_new = Vec::new();
for customs_id in 1..=first.noncitizen.count {
if customs
.noncitizens
.iter()
.any(|x| x.id == customs_id && x.citizenship == CitizenshipType::NonCitizen)
{
continue;
}
let new = Officer::new(
customs_id,
first.noncitizen.time,
CitizenshipType::NonCitizen,
);
noncitizen_customs_new.push(new);
}
customs.citizens.extend(citizen_customs_new);
customs.noncitizens.extend(noncitizen_customs_new);
Ok(Output::new(customs, citizens))
}

2
src/utils.rs Normal file
View File

@ -0,0 +1,2 @@
pub type Time = u32;
pub type CustomsCount = u8;

47
tests/test.rs Normal file
View File

@ -0,0 +1,47 @@
use customs_rs::process::process;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn customs_1() {
let input = include_str!("../data/input/customs.i1");
let output = include_str!("../data/output/customs.o1");
assert_eq!(process(input), output);
}
#[test]
fn customs_2() {
let input = include_str!("../data/input/customs.i2");
let output = include_str!("../data/output/customs.o2");
assert_eq!(process(input), output);
}
#[test]
fn customs_3() {
let input = include_str!("../data/input/customs.i3");
let output = include_str!("../data/output/customs.o3");
assert_eq!(process(input), output);
}
#[test]
fn customs_4() {
let input = include_str!("../data/input/customs.i4");
let output = include_str!("../data/output/customs.o4");
assert_eq!(process(input), output);
}
#[test]
fn customs_5() {
let input = include_str!("../data/input/customs.i5");
let output = include_str!("../data/output/customs.o5");
assert_eq!(process(input), output);
}
#[test]
fn customs_6() {
let input = include_str!("../data/input/customs.i6");
let output = include_str!("../data/output/customs.o6");
assert_eq!(process(input), output);
}
#[test]
fn customs_7() {
let input = include_str!("../data/input/customs.i7");
let output = include_str!("../data/output/customs.o7");
assert_eq!(process(input), output);
}
}