1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
//! # Duet
//!
//! Reverse engineering the code shows that the program is broken into 2 sections.
//! Each input differs only in the number specified on line 10.
//!
//! The first section is only executed by program 0 and generates a pseudorandom sequence of
//! 127 numbers between 0 and 9999. The programs then take turns implementing the innner loop of
//! the [imfamous bubble sort](https://en.wikipedia.org/wiki/Bubble_sort) in descending order.
//!
//! The partially sorted sequence is passed back and forth between each program until in final
//! sorted order. Assembly code annotated with Rust pseduocode:
//!
//! ```none
//!     set i 31
//!     set a 1
//!     mul p 17
//!     jgz p p         if p == 0 {
//!     mul a 2             let a = 2 ^ 31 - 1 = 0x7fffffff;
//!     add i -1
//!     jgz i -2
//!     add a -1
//!     set i 127
//!     set p SEED          let mut p = SEED;
//!     mul p 8505          for _ in 0..127 {
//!     mod p a                 p = (p * 8505) % a;
//!     mul p 129749
//!     add p 12345
//!     mod p a                 p = (p * 129749 + 12345) % a;
//!     set b p
//!     mod b 10000
//!     snd b                   send(p % 10000)
//!     add i -1
//!     jgz i -9            }
//!     jgz a 3         }
//!     rcv b           // These two lines deadlock the program
//!     jgz b -1        // once the list is sorted.
//!     set f 0         while swapped { // f is 0 when list is sorted
//!     set i 126           a = receive();
//!     rcv a               for _ in 0..126 {
//!     rcv b                   b = receive();
//!     set p a
//!     mul p -1
//!     add p b
//!     jgz p 4                 if b <= a {
//!     snd a                       send(a);
//!     set a b                     a = b;
//!     jgz 1 3                 } else {
//!     snd b                       send(b);
//!     set f 1                     swapped = true;
//!     add i -1                }
//!     jgz i -11           }
//!     snd a
//!     jgz f -16       }
//!     jgz a -19       // Jump to deadlock section.
//! ```
use crate::util::parse::*;

/// Generate a pseudorandom sequence of 127 numbers, based on a
/// starting seed different for each input.
pub fn parse(input: &str) -> Vec<u64> {
    // Read the starting seed from the input.
    let mut p: u64 = input.lines().nth(9).unwrap().unsigned();
    let mut numbers = Vec::with_capacity(127);

    // Generate pseudorandom sequence.
    for _ in 0..127 {
        p = (p * 8505) % 0x7fffffff;
        p = (p * 129749 + 12345) % 0x7fffffff;
        numbers.push(p % 10000);
    }

    numbers
}

/// Part one is the last number sent in the sequence.
pub fn part1(input: &[u64]) -> u64 {
    input[126]
}

/// Bubble sort the sequence into descending order, counting the number of passes.
/// Starting with program 1 each program alternates sorting the input by sending it to the other
/// program, so the number of passes that program 1 takes is the total divided by two rounding up.
pub fn part2(input: &[u64]) -> usize {
    let mut numbers = input.to_vec();
    let mut swapped = true;
    let mut count = 0;

    // Bubble sort in descending order.
    while swapped {
        swapped = false;

        // "Optimized" version skipping the last count elements as these are already sorted.
        for i in 1..127 - count {
            if numbers[i - 1] < numbers[i] {
                numbers.swap(i - 1, i);
                swapped = true;
            }
        }

        count += 1;
    }

    // The sequence countains 127 numbers so the final result is multiplied by that factor.
    127 * count.div_ceil(2)
}