1#[cfg(test)]
37#[macro_use]
38extern crate pretty_assertions;
39
40mod money;
41
42use std::collections::BTreeMap;
43use std::collections::BTreeSet;
44use std::fmt;
45
46use crate::money::Money;
47
48#[derive(Debug, PartialEq, Eq)]
49struct Record {
50 creditor: String,
51 amount: Money,
52 debtors: BTreeSet<String>,
53}
54
55struct Records {
56 records: Vec<Record>,
57}
58
59#[derive(Debug, Eq, PartialEq)]
60pub struct Debt {
61 pub debtor: String,
62 pub amount: Money,
63 pub creditor: String,
64}
65
66pub fn run<T: IntoIterator<Item = String>>(lines: T) -> Vec<Debt> {
67 Records::new(lines).calc_debt_resolution()
68}
69
70fn normalize_input<T: IntoIterator<Item = String>>(lines: T) -> Vec<Record> {
71 let mut records = Vec::<Record>::default();
72 let mut lines = lines.into_iter().filter(|s| !s.is_empty());
73 while let Some(line) = lines.next() {
74 let mut tokens = line.split_whitespace().take_while(|&token| token != "#");
75 let Some(creditor) = tokens.next() else {
76 continue;
77 };
78 let Some(amount) = tokens.next() else {
79 continue;
80 };
81 let r = Record {
82 creditor: creditor.into(),
83 amount: money::parse(amount),
84 debtors: tokens.map(|s| s.to_owned()).collect(),
85 };
86 records.push(r);
87 }
88 let participants: BTreeSet<String> = records.iter().fold(BTreeSet::new(), |mut memo, elem| {
89 memo.insert(elem.creditor.to_owned());
90 memo.extend(elem.debtors.clone());
91 memo
92 });
93 let creditors: BTreeSet<String> = records.iter().map(|rec| rec.creditor.clone()).collect();
94 let debtors_with_no_credit = participants.difference(&creditors).map(|debtor| Record {
97 creditor: debtor.clone(),
98 amount: money::zero(),
99 debtors: BTreeSet::new(),
100 });
101 records
102 .into_iter()
103 .chain(debtors_with_no_credit)
104 .map(|record| {
105 let debtors: BTreeSet<String> = if record.debtors.is_empty() {
106 &participants
107 } else {
108 &record.debtors
109 }
110 .iter()
111 .cloned()
112 .collect();
113 Record {
114 debtors: debtors,
115 ..record
116 }
117 })
118 .collect()
119}
120
121fn sum<'a, I>(amounts: I) -> Money
122where
123 I: IntoIterator<Item = &'a Money>,
124{
125 amounts
126 .into_iter()
127 .fold(money::zero(), |memo, elem| memo + elem)
128}
129
130impl Records {
131 fn new<T: IntoIterator<Item = String>>(records_init: T) -> Records {
132 Records {
133 records: normalize_input(records_init),
134 }
135 }
136
137 fn calc_expenses_per_person2<R: AsRef<Record>>(records: &[R]) -> BTreeMap<String, Money> {
138 records
139 .iter()
140 .filter(|record| record.as_ref().amount != money::zero())
141 .fold(BTreeMap::new(), |mut memo, record| {
142 let record = record.as_ref();
143 {
144 let amount = memo
145 .entry(record.creditor.clone())
146 .or_insert_with(money::zero);
147 *amount = amount.clone() + &record.amount;
148 }
149 memo
150 })
151 }
152
153 fn calc_expenses_per_person_and_group(
154 &self,
155 ) -> BTreeMap<BTreeSet<String>, BTreeMap<String, Money>> {
156 self.records_by_group()
157 .iter()
158 .map(|(group, records)| (group.clone(), Self::calc_expenses_per_person2(records)))
159 .collect()
160 }
161
162 fn calc_share(records: &[&Record], group_size: usize) -> Money {
163 sum(records.iter().map(|r| &r.amount)) / money::from(group_size)
164 }
165
166 fn calc_share_per_person_and_group(
167 &self,
168 ) -> BTreeMap<BTreeSet<String>, BTreeMap<String, Money>> {
169 self.records_by_group()
170 .iter()
171 .map(|(group, records)| {
172 let share = Self::calc_share(records, group.len());
173 let share_per_person = records
174 .iter()
175 .flat_map(|rec| {
176 rec.debtors
177 .iter()
178 .map(|debtor| (debtor.clone(), share.clone()))
179 })
180 .collect();
181 (group.clone(), share_per_person)
182 })
183 .collect()
184 }
185
186 fn records_by_group(&self) -> BTreeMap<BTreeSet<String>, Vec<&Record>> {
187 self.records.iter().fold(BTreeMap::new(), |mut memo, elem| {
188 memo.entry(elem.debtors.clone())
189 .or_insert_with(|| vec![])
190 .push(elem);
191 memo
192 })
193 }
194
195 fn expenses_creditor_not_part_of_group(&self) -> BTreeMap<String, Money> {
196 self.records
197 .iter()
198 .filter(|&rec| !rec.debtors.contains(&rec.creditor))
199 .fold(Default::default(), |mut memo, record| {
200 let entry = memo.entry(record.creditor.clone()).or_insert(money::zero());
201 *entry = &*entry + &record.amount * money::from(-1);
202 memo
203 })
204 }
205
206 fn calc_debt_per_person(&self) -> BTreeMap<String, Money> {
207 let share_per_person_and_group = self.calc_share_per_person_and_group();
208 let expenses_per_person_group = self.calc_expenses_per_person_and_group();
209 share_per_person_and_group
210 .into_iter()
211 .map(|(group, share_per_person)| {
212 let expenses_per_person = &expenses_per_person_group[&group];
213 share_per_person
214 .into_iter()
215 .map(|(person, share)| {
216 let debt =
217 share - expenses_per_person.get(&person).unwrap_or(&money::zero());
218 (person.clone(), debt)
219 })
220 .collect::<BTreeMap<String, Money>>()
221 })
222 .chain(vec![self.expenses_creditor_not_part_of_group()].into_iter())
223 .fold(BTreeMap::new(), |mut memo, debt_per_person| {
224 for (person, debt) in debt_per_person {
225 let debt_acc = memo.entry(person).or_insert_with(money::zero);
226 *debt_acc = debt_acc.clone() + debt;
227 }
228 memo
229 })
230 }
231
232 fn calc_debt_resolution(&self) -> Vec<Debt> {
233 let debt_per_person = self.calc_debt_per_person();
234 let debt_per_person_by_debt = {
235 let mut d: Vec<(String, Money)> = debt_per_person
236 .iter()
237 .map(|(k, v)| (k.clone(), v.clone()))
238 .filter(|&(_, ref v)| v > &money::zero())
239 .collect();
240 d.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
241 d
242 };
243 let mut expense_per_person = {
244 let mut d: Vec<(String, Money)> = debt_per_person
245 .iter()
246 .map(|(k, v)| (k.clone(), v.clone()))
247 .filter(|&(_, ref v)| v < &money::zero())
248 .collect();
249 d.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
250 d
251 };
252 debt_per_person_by_debt
253 .into_iter()
254 .flat_map(|(person, debt)| {
255 self.resolve_for_person(&person, &debt, &mut expense_per_person)
256 })
257 .collect()
258 }
259
260 fn resolve_for_person(
261 &self,
262 person: &str,
263 debt: &Money,
264 expense_per_person: &mut [(String, Money)],
265 ) -> Vec<Debt> {
266 let mut debt = debt.clone();
267 let mut payouts = vec![];
268 let zero: Money = money::zero();
269 let mut last_debt = money::zero();
270 while &debt != &last_debt && &debt != &zero {
271 last_debt = debt.clone();
272 let pos_opt = expense_per_person
273 .iter()
274 .position(|x| x.0 != person && &x.1 < &zero);
275 if let Some(pos) = pos_opt {
276 let (ref creditor, ref mut expense) = expense_per_person[pos];
277 let remainder = &debt + expense.clone();
278 if &remainder >= &zero {
279 debt = remainder;
280 payouts.push((creditor.clone(), expense.clone() * money::from(-1)));
281 *expense = money::zero();
282 } else if &remainder < &zero {
283 *expense = remainder;
284 payouts.push((creditor.clone(), debt.clone()));
285 debt = money::zero();
286 }
287 } else {
288 break;
289 }
290 }
291 payouts
292 .into_iter()
293 .map(|(creditor, amount)| Debt {
294 debtor: person.to_owned(),
295 amount: amount,
296 creditor: creditor,
297 })
298 .collect()
299 }
300}
301
302impl fmt::Display for Record {
303 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
304 let debtors: Vec<_> = self.debtors.iter().map(|x| x.as_str()).collect();
305 write!(
306 f,
307 "Creditor: {}, Amount: {}, Debtors {}",
308 self.creditor,
309 self.amount,
310 debtors.as_slice().join(", ")
311 )
312 }
313}
314
315impl fmt::Display for Debt {
316 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
317 let amount = money::to_float(&self.amount);
318 write!(f, "{} owes {} {:.2}", self.debtor, self.creditor, amount)
319 }
320}
321
322impl AsRef<Record> for Record {
323 fn as_ref(&self) -> &Record {
324 self
325 }
326}
327
328#[cfg(test)]
329mod tests {
330 use std::collections::BTreeSet;
331 use std::fmt;
332 use std::str::FromStr;
333
334 use super::money;
335 use super::normalize_input;
336 use super::run;
337 use super::Debt;
338 use super::Record;
339 use super::Records;
340
341 #[test]
342 fn test_str2btree_set() {
343 let actual = str2btree_set("a b c");
344 let mut expected = BTreeSet::new();
345 expected.insert("a".into());
346 expected.insert("b".into());
347 expected.insert("c".into());
348 assert_eq!(actual, expected);
349 }
350
351 #[test]
352 fn test_normalize_input() {
353 let actual = normalize_input(get_input());
354 let expected = vec![
355 Record::new("a", "1200", "a b c"),
356 Record::new("b", "600", "a b c"),
357 Record::new("b", "200", "b c"),
358 Record::new("c", "100", "a c"),
359 ];
360 assert_eq!(actual, expected);
361 }
362
363 #[test]
364 fn test_normalize_input_no_debtors() {
365 let actual = normalize_input("a 100\nb 100\n\n".lines().map(|x| x.to_owned()));
366 let expected = vec![
367 Record::new("a", "100", "a b"),
368 Record::new("b", "100", "a b"),
369 ];
370 assert_eq!(actual, expected);
371 }
372
373 #[test]
374 fn test_calc_share_per_person_and_group() {
375 let records = Records::new(get_input());
376 let actual = records.calc_share_per_person_and_group();
377 assert_eq!(3, actual.len());
378 let expected = vec![
379 (
380 str2btree_set("a b c"),
381 vec![
382 ("a".into(), parse("600")),
383 ("b".into(), parse("600")),
384 ("c".into(), parse("600")),
385 ]
386 .into_iter()
387 .collect(),
388 ),
389 (
390 str2btree_set("a c"),
391 vec![("a".into(), parse("50")), ("c".into(), parse("50"))]
392 .into_iter()
393 .collect(),
394 ),
395 (
396 str2btree_set("b c"),
397 vec![("b".into(), parse("100")), ("c".into(), parse("100"))]
398 .into_iter()
399 .collect(),
400 ),
401 ]
402 .into_iter()
403 .collect();
404 assert_eq!(actual, expected);
405 }
406
407 #[test]
408 fn test_calc_expenses_per_person_and_group() {
409 let records = Records::new(get_input());
410 let actual = records.calc_expenses_per_person_and_group();
411 let expected = vec![
412 (
413 str2btree_set("a b c"),
414 vec![("a".into(), parse("1200")), ("b".into(), parse("600"))]
415 .into_iter()
416 .collect(),
417 ),
418 (
419 str2btree_set("a c"),
420 vec![("c".into(), parse("100"))].into_iter().collect(),
421 ),
422 (
423 str2btree_set("b c"),
424 vec![("b".into(), parse("200"))].into_iter().collect(),
425 ),
426 ]
427 .into_iter()
428 .collect();
429 assert_eq!(actual, expected);
430 }
431
432 #[test]
433 fn test_calc_debt_per_person() {
434 let records = Records::new(get_input());
435 let actual = records.calc_debt_per_person();
436 let expected = vec![
437 ("a".into(), parse("-550")),
438 ("b".into(), parse("-100")),
439 ("c".into(), parse("650")),
440 ]
441 .into_iter()
442 .collect();
443 assert_eq!(actual, expected);
444 }
445
446 #[test]
447 fn test_calc_debt_resolution() {
448 let records = Records::new(get_input());
449 let actual = records.calc_debt_resolution();
450 let expected = vec![
451 Debt {
452 debtor: "c".into(),
453 amount: parse("550"),
454 creditor: "a".into(),
455 },
456 Debt {
457 debtor: "c".into(),
458 amount: parse("100"),
459 creditor: "b".into(),
460 },
461 ];
462 assert_eq!(actual, expected);
463 }
464
465 #[test]
466 fn test_run() {
467 let actual = run(get_input());
468 let expected = vec![
469 Debt {
470 debtor: "c".into(),
471 amount: parse("550"),
472 creditor: "a".into(),
473 },
474 Debt {
475 debtor: "c".into(),
476 amount: parse("100"),
477 creditor: "b".into(),
478 },
479 ];
480 assert_eq!(actual, expected);
481 }
482
483 #[test]
484 fn test_only_one_expenser() {
485 let actual = run(to_input("a 100 b"));
486 let expected = vec![mk_debt("b 100 a")];
487 assert_eq!(actual, expected);
488 }
489
490 #[test]
491 fn test_a_to_b_and_b_to_c() {
492 let actual = run(to_input("a 100 b\nb 50 c"));
493 let expected = vec![mk_debt("b 50 a"), mk_debt("c 50 a")];
494 assert_eq!(actual, expected);
495 }
496
497 #[test]
498 fn test_fractions() {
499 let actual = stringify(run(to_input("a 33.33 b c\nb 12.33 a c")));
500 let expected = stringify(vec![mk_debt("c 22.83 a"), mk_debt("b 4.33 a")]);
501 assert_eq!(expected, actual);
502 }
503
504 #[test]
505 fn test_comment_in_input() {
506 let input = to_input(
507 "
508 d 100 b # ignore this
509 d 200 a
510 # also ignore this
511 d 300 c",
512 );
513 let repay_calc = run(input.clone());
514 let actual = stringify(repay_calc);
515 assert_eq!(
516 actual,
517 ["c owes d 300.00", "a owes d 200.00", "b owes d 100.00"]
518 );
519 }
520
521 #[test]
522 fn test_buggy_case() {
523 let input = to_input(
524 "
525 d 291 b
526 d 286 a
527 d 301 c
528 d 393 d e
529 b 1965 e b d a c",
530 );
531 let mut repay_calc = run(input.clone());
532 let actual_sum: f64 = repay_calc.iter().map(|x| money::to_float(&x.amount)).sum();
533 let expected_sum: f64 = [694.0, 589.5, 587.0, 92.0].iter().sum();
534 assert_eq!(expected_sum, actual_sum);
535
536 let expected = [
537 "c owes b 694.00",
538 "e owes d 589.50",
539 "a owes b 587.00",
540 "a owes d 92.00",
541 ];
542 repay_calc.sort_by_key(|debt| (&debt.amount * money::from(-1), debt.debtor.clone()));
543 let actual = stringify(repay_calc);
544 assert_eq!(expected, &actual[..]);
545 }
546
547 fn to_input(s: &str) -> Vec<String> {
548 s.lines().map(|x| x.to_owned()).collect()
549 }
550
551 fn get_input() -> Vec<String> {
552 to_input(INPUT_1)
553 }
554
555 const INPUT_1: &'static str = "\
556 a 1200 a b c\n\
557 b 600 a b c\n\
558 b 200 b c\n\
559 c 100 a c\n\
560 ";
561
562 #[test]
563 fn test_input() {
564 assert_eq!(INPUT_1.lines().count(), 4);
565 }
566
567 fn mk_debt(debt: &str) -> Debt {
568 let mut itr = debt.split_whitespace();
569 let d = itr.next().unwrap();
570 let amt = itr.next().unwrap();
571 let c = itr.next().unwrap();
572 Debt {
573 debtor: d.into(),
574 amount: money::parse(amt).reduced(),
575 creditor: c.into(),
576 }
577 }
578
579 fn parse<T>(s: &str) -> T
580 where
581 T: FromStr,
582 T::Err: fmt::Debug,
583 {
584 s.parse().unwrap()
585 }
586
587 impl Record {
588 fn new(creditor: &str, amount: &str, debtors_init: &str) -> Record {
589 Record {
590 creditor: creditor.into(),
591 amount: parse(amount),
592 debtors: str2btree_set(debtors_init),
593 }
594 }
595 }
596
597 fn str2btree_set(xs: &str) -> BTreeSet<String> {
598 let mut out = BTreeSet::new();
599 out.extend(xs.split_whitespace().map(|x| x.to_owned()));
600 out
601 }
602
603 fn stringify(xs: Vec<Debt>) -> Vec<String> {
604 xs.into_iter().map(|x| x.to_string()).collect()
605 }
606}