summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGard Spreemann <gspr@nonempty.org>2021-12-07 15:24:17 +0100
committerGard Spreemann <gspr@nonempty.org>2021-12-07 15:24:17 +0100
commit57d5137b5283d21eea9be8f0ac1bdd6160766a00 (patch)
treecfd7bd431378587e35fa6fcde826c014e7c59bb7
parentd78330327d40fb0146aef5f8c2562ba5a2752e9e (diff)
Day 4
-rw-r--r--04/Cargo.toml15
-rw-r--r--04/input.txt601
-rw-r--r--04/src/part-1.rs90
-rw-r--r--04/src/part-2.rs95
-rw-r--r--04/src/tensor.rs156
-rw-r--r--04/test.txt20
6 files changed, 977 insertions, 0 deletions
diff --git a/04/Cargo.toml b/04/Cargo.toml
new file mode 100644
index 0000000..b98bd9d
--- /dev/null
+++ b/04/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "day-04"
+version = "0.1.0"
+authors = ["Gard Spreemann <gspr@nonempty.org>"]
+edition = "2021"
+
+[[bin]]
+name = "part-1"
+path = "src/part-1.rs"
+
+[[bin]]
+name = "part-2"
+path = "src/part-2.rs"
+
+[dependencies]
diff --git a/04/input.txt b/04/input.txt
new file mode 100644
index 0000000..ee55530
--- /dev/null
+++ b/04/input.txt
@@ -0,0 +1,601 @@
+27,14,70,7,85,66,65,57,68,23,33,78,4,84,25,18,43,71,76,61,34,82,93,74,26,15,83,64,2,35,19,97,32,47,6,51,99,20,77,75,56,73,80,86,55,36,13,95,52,63,79,72,9,10,16,8,69,11,50,54,81,22,45,1,12,88,44,17,62,0,96,94,31,90,39,92,37,40,5,98,24,38,46,21,30,49,41,87,91,60,48,29,59,89,3,42,58,53,67,28
+
+31 23 52 26 8
+27 89 37 80 46
+97 19 63 34 79
+13 59 45 12 73
+42 25 22 6 39
+
+27 71 24 3 0
+79 42 32 72 62
+99 52 11 92 33
+38 22 16 44 39
+35 26 76 49 58
+
+39 19 82 53 57
+52 98 69 77 23
+ 1 40 18 66 83
+34 85 28 48 16
+15 93 38 96 27
+
+74 50 88 84 99
+34 2 11 25 17
+57 4 19 83 1
+59 77 42 36 33
+73 22 23 37 55
+
+98 91 56 84 78
+45 21 24 83 40
+46 58 8 67 4
+33 97 55 7 86
+ 2 68 64 27 69
+
+68 29 14 49 26
+ 4 21 87 71 32
+58 5 17 46 93
+45 96 8 83 2
+78 91 9 20 42
+
+49 81 19 48 37
+38 23 45 82 92
+93 99 67 66 42
+40 74 25 56 16
+21 47 26 75 61
+
+53 66 72 30 34
+55 82 77 6 92
+60 56 8 22 88
+ 5 71 49 29 74
+28 2 32 84 73
+
+52 31 24 68 41
+48 82 19 29 65
+51 91 97 39 80
+ 3 55 43 40 38
+20 89 53 45 75
+
+29 74 19 89 18
+32 88 93 46 63
+91 4 94 64 5
+57 54 49 36 40
+97 81 39 77 1
+
+ 7 57 94 84 39
+92 3 28 15 75
+88 45 65 81 63
+86 4 89 37 71
+ 8 13 66 42 85
+
+60 66 35 47 98
+96 27 40 51 39
+ 3 64 25 28 74
+58 17 97 59 29
+95 31 18 44 37
+
+ 3 31 97 85 71
+79 82 22 61 98
+87 14 17 66 75
+36 89 88 83 63
+44 8 81 25 48
+
+73 84 28 90 94
+25 19 44 10 23
+ 8 59 17 9 93
+20 77 97 64 6
+98 82 27 70 91
+
+18 51 16 99 2
+58 22 89 13 19
+39 66 91 8 32
+49 24 85 94 42
+45 70 10 86 4
+
+23 81 66 13 34
+25 80 97 5 42
+79 35 2 78 9
+ 0 6 91 94 45
+21 90 76 50 56
+
+50 92 2 96 75
+85 82 80 97 31
+61 35 55 27 56
+74 42 9 29 90
+86 15 88 47 1
+
+18 20 54 92 62
+45 22 32 61 75
+ 1 38 50 81 42
+82 4 21 77 65
+27 51 56 39 48
+
+36 10 62 28 70
+94 99 34 54 6
+15 1 41 13 12
+92 52 2 63 82
+90 64 29 69 32
+
+23 77 33 90 17
+45 78 5 67 28
+57 73 89 81 21
+49 64 37 15 14
+ 7 59 4 43 16
+
+81 92 25 28 90
+93 72 43 94 26
+24 9 13 74 10
+21 2 36 32 51
+87 97 55 86 71
+
+82 71 99 17 90
+69 95 65 55 10
+ 9 92 39 62 78
+59 13 61 24 44
+ 8 31 58 0 57
+
+17 83 55 99 27
+79 4 33 76 7
+81 43 44 49 72
+ 2 48 97 20 77
+47 60 35 16 63
+
+93 95 94 1 98
+61 57 84 55 22
+85 40 65 46 59
+21 15 63 77 7
+13 99 49 3 96
+
+ 8 21 14 45 41
+65 63 82 62 28
+91 44 22 79 96
+20 75 86 3 26
+74 11 42 59 36
+
+ 5 52 43 92 99
+46 63 10 45 81
+13 66 21 32 89
+25 28 96 40 88
+27 18 31 73 34
+
+ 3 26 43 32 36
+68 87 67 65 99
+73 61 20 90 7
+21 52 2 82 10
+58 49 56 16 80
+
+97 25 93 63 32
+87 14 5 22 76
+89 92 91 3 51
+ 0 24 95 69 20
+96 11 10 1 55
+
+95 86 44 75 70
+59 76 45 2 99
+ 1 34 71 81 41
+87 14 33 84 96
+ 8 38 9 82 68
+
+27 71 70 75 76
+25 87 2 79 96
+20 88 50 37 32
+48 94 63 86 22
+15 6 34 78 59
+
+30 89 51 31 77
+74 10 86 71 84
+29 54 58 44 5
+11 90 26 50 63
+64 62 20 40 46
+
+37 9 46 23 31
+68 21 25 36 90
+17 33 6 50 30
+11 89 20 47 60
+26 59 34 62 77
+
+84 52 40 97 7
+88 30 42 58 94
+64 10 2 90 83
+44 35 77 91 47
+14 74 9 78 53
+
+86 14 0 39 24
+87 69 58 8 73
+88 74 27 40 51
+63 54 55 93 61
+16 66 15 21 48
+
+43 70 9 81 42
+36 54 99 34 95
+98 19 90 25 44
+69 56 18 77 49
+58 16 67 75 57
+
+36 44 14 98 23
+31 5 83 46 3
+45 21 41 11 60
+33 81 88 92 65
+13 51 48 59 71
+
+12 5 70 87 32
+42 18 90 73 88
+68 29 76 38 55
+67 62 15 77 34
+39 27 51 54 19
+
+87 8 92 93 88
+77 54 15 1 43
+35 97 26 21 29
+13 46 96 69 47
+51 38 91 32 63
+
+73 99 30 15 16
+42 58 21 88 44
+45 13 27 68 9
+36 6 81 53 5
+78 76 11 60 1
+
+57 76 50 78 31
+45 42 68 53 16
+ 9 88 89 19 21
+96 61 97 69 34
+98 87 33 82 0
+
+ 4 16 89 57 64
+46 75 77 65 23
+71 42 96 52 38
+ 1 21 93 0 35
+59 80 53 36 58
+
+97 62 35 1 88
+98 60 17 45 94
+12 43 65 23 19
+71 52 3 40 59
+50 76 61 20 22
+
+92 65 38 93 13
+55 26 10 46 29
+85 23 19 74 34
+60 14 27 36 18
+53 4 52 49 17
+
+99 56 93 70 28
+25 0 77 80 57
+91 50 72 76 23
+53 58 95 78 59
+75 85 90 44 9
+
+30 8 5 60 6
+28 35 59 70 96
+20 99 98 81 79
+94 78 27 71 4
+ 7 34 43 46 51
+
+93 65 22 69 33
+92 49 75 35 11
+58 39 62 86 83
+64 4 76 48 82
+74 1 56 95 31
+
+ 1 78 98 90 55
+80 14 36 99 7
+85 8 10 9 92
+76 11 40 70 62
+43 53 74 35 58
+
+46 78 35 28 49
+84 73 65 25 34
+40 59 66 36 67
+16 22 29 0 45
+20 56 39 88 91
+
+32 58 35 25 79
+78 94 57 38 14
+89 87 68 48 76
+ 7 67 40 51 33
+95 31 43 93 92
+
+38 21 82 31 23
+54 16 77 37 42
+73 99 7 34 90
+71 26 5 91 52
+22 27 47 85 62
+
+ 2 86 28 37 55
+ 1 82 9 36 31
+52 98 89 30 60
+13 17 63 38 57
+73 50 42 20 12
+
+56 3 67 62 35
+59 39 19 22 27
+21 58 57 41 54
+75 13 82 50 32
+23 5 99 66 10
+
+ 7 19 45 66 78
+38 57 40 73 87
+58 30 99 53 83
+64 1 8 56 95
+70 77 16 18 82
+
+72 83 95 37 35
+54 59 92 21 79
+ 7 81 86 29 41
+52 99 42 57 71
+ 3 15 75 34 77
+
+ 7 70 5 69 4
+34 60 40 73 6
+74 54 67 32 38
+93 62 17 51 86
+57 88 99 3 16
+
+42 74 11 34 7
+82 47 71 31 58
+69 23 43 4 64
+32 19 98 93 41
+63 97 8 85 48
+
+63 54 34 38 86
+ 4 27 15 49 0
+61 77 53 98 74
+62 23 88 97 37
+93 28 25 50 13
+
+56 82 41 27 79
+23 31 64 7 65
+52 98 93 16 57
+88 49 10 11 62
+43 95 53 51 83
+
+41 10 87 54 86
+19 22 13 40 17
+37 27 45 29 63
+83 85 81 90 7
+57 88 47 66 56
+
+67 44 54 88 89
+20 46 61 28 92
+86 49 60 83 95
+42 78 97 51 96
+11 62 4 26 31
+
+18 68 87 26 70
+62 84 11 33 90
+ 0 45 66 83 6
+20 19 27 44 55
+52 8 5 7 3
+
+54 94 88 76 92
+13 98 22 33 26
+95 62 53 81 24
+29 69 15 87 25
+61 40 84 90 93
+
+ 7 31 3 28 46
+20 51 21 18 38
+30 92 39 70 61
+27 88 35 96 74
+23 5 66 11 42
+
+40 61 90 57 54
+41 14 99 62 59
+92 10 48 81 52
+22 29 77 18 87
+31 79 25 94 13
+
+17 26 44 98 57
+74 83 51 14 11
+76 91 96 64 33
+43 45 92 72 27
+66 3 28 20 40
+
+88 82 44 71 55
+83 47 51 76 24
+86 19 42 34 99
+30 31 87 48 62
+98 53 68 9 21
+
+ 3 31 6 41 61
+24 77 81 96 44
+78 73 1 98 11
+40 80 27 65 92
+62 67 2 30 10
+
+78 46 50 65 56
+84 16 32 58 86
+22 12 54 99 35
+ 9 43 55 10 94
+66 81 59 92 76
+
+78 3 55 23 83
+13 42 94 91 22
+14 37 31 67 71
+ 8 61 57 34 43
+74 50 0 39 65
+
+78 16 13 91 34
+14 74 86 3 97
+12 89 58 65 51
+29 57 48 44 93
+95 1 42 39 92
+
+93 96 16 85 25
+59 3 70 19 17
+21 84 58 38 86
+57 10 35 95 79
+81 44 73 63 9
+
+22 1 96 7 93
+40 49 2 4 66
+87 21 17 32 48
+44 28 42 99 26
+69 8 85 86 75
+
+21 31 37 87 28
+89 43 74 83 57
+95 29 92 88 35
+94 25 97 81 50
+15 19 73 45 63
+
+92 62 67 95 57
+30 8 4 39 64
+99 31 70 63 96
+25 53 24 93 35
+34 51 82 91 28
+
+41 30 20 56 46
+16 32 98 60 35
+67 9 43 42 88
+78 90 71 5 29
+49 31 37 63 18
+
+80 40 88 5 62
+ 3 6 74 71 97
+19 58 63 59 38
+50 64 34 68 45
+25 30 21 33 83
+
+10 65 67 17 50
+21 51 18 68 59
+29 78 77 99 76
+62 35 96 7 95
+82 53 42 49 69
+
+74 65 89 6 1
+18 30 72 75 24
+60 50 52 55 82
+68 99 4 61 22
+ 9 37 84 57 87
+
+96 85 56 72 2
+ 9 38 98 12 4
+34 45 74 97 86
+18 94 64 70 68
+91 41 58 39 66
+
+34 13 26 80 29
+ 0 4 21 60 90
+39 73 12 2 19
+64 44 61 88 45
+59 50 8 91 49
+
+34 85 55 2 75
+10 15 89 12 63
+90 29 87 73 71
+38 17 84 45 9
+97 98 77 23 61
+
+47 43 22 58 1
+63 44 2 94 99
+33 81 51 49 13
+38 86 42 91 23
+ 7 67 68 39 84
+
+ 4 26 12 38 41
+43 16 88 71 99
+50 24 19 77 98
+23 73 44 10 51
+56 42 30 52 59
+
+57 16 9 62 27
+26 65 56 10 82
+ 0 74 78 12 99
+77 18 38 5 37
+ 7 60 40 90 23
+
+14 69 18 51 8
+21 79 60 36 12
+68 44 59 45 16
+90 50 85 25 70
+91 31 30 54 26
+
+24 40 51 72 63
+31 60 62 25 96
+ 9 44 35 28 91
+97 4 34 81 2
+61 68 94 52 86
+
+ 0 57 95 88 94
+36 38 25 35 19
+13 6 8 61 98
+45 85 86 69 97
+41 32 7 15 59
+
+41 82 19 29 34
+44 96 6 91 76
+69 21 32 94 98
+ 4 10 88 30 2
+ 8 74 56 65 99
+
+36 91 73 15 54
+62 55 40 27 44
+11 60 95 61 46
+31 32 21 41 35
+74 86 83 89 79
+
+ 2 96 94 82 68
+39 83 49 30 15
+62 11 86 99 59
+51 80 12 72 58
+87 66 98 53 29
+
+44 71 18 63 85
+11 75 60 66 13
+36 9 94 57 8
+10 12 32 3 86
+ 4 29 54 70 21
+
+27 33 76 83 67
+77 29 65 39 44
+52 34 25 93 64
+35 4 57 92 84
+41 51 88 96 0
+
+21 91 82 3 26
+23 8 36 20 73
+54 39 60 34 57
+49 99 97 69 43
+41 93 95 80 63
+
+73 77 4 9 22
+17 33 15 86 79
+38 16 99 98 30
+64 92 76 50 68
+83 85 52 87 88
+
+57 53 13 36 76
+ 7 10 91 3 22
+ 8 84 56 73 59
+62 80 85 38 33
+68 97 47 14 96
+
+36 8 98 43 70
+85 95 31 1 51
+33 41 78 89 56
+76 16 15 34 82
+12 18 39 4 3
+
+98 49 41 30 95
+68 89 81 48 84
+15 19 90 66 14
+32 1 88 34 64
+73 65 6 20 86
+
+22 18 13 74 34
+75 4 60 88 46
+25 97 54 94 91
+42 67 40 11 81
+ 5 12 49 48 15
+
+82 91 18 73 57
+97 50 34 16 66
+29 43 81 20 15
+19 44 85 4 32
+90 58 39 53 42
+
+50 53 83 10 0
+93 16 84 23 13
+89 63 75 69 51
+65 35 67 56 70
+ 4 37 29 47 38
diff --git a/04/src/part-1.rs b/04/src/part-1.rs
new file mode 100644
index 0000000..3852036
--- /dev/null
+++ b/04/src/part-1.rs
@@ -0,0 +1,90 @@
+mod tensor;
+
+use std::io::{BufRead};
+use std::collections::{HashMap};
+use std::ops::{IndexMut};
+
+pub type Matrix = tensor::Tensor<(bool, u64), 2>;
+pub type Index = tensor::Index<2>;
+
+fn read_board(handle: &mut std::io::StdinLock) -> Option<Matrix> {
+ let mut lines = handle.lines();
+ let first_line = lines.next()?.ok()?;
+ let mut words = first_line.split_whitespace();
+ let mut data: Vec<(bool, u64)> = words.map(|x| (false, x.parse().expect("Malformed input"))).collect();
+ let n = data.len();
+ for i in 1..n {
+ let line = lines.next().expect("Malformed input").expect("IO error");
+ let mut words = line.split_whitespace();
+ data.extend(words.map(|x| (false, x.parse::<u64>().expect("Malformed input"))));
+ }
+ lines.next();
+ Some(Matrix::new_from([n, n], data))
+}
+
+fn is_board_winning(board: & Matrix, pivot: Index) -> bool {
+ let mut win_row = true;
+ for j in 0..board.shape()[1] {
+ if !board[[pivot[0], j]].0 { win_row = false; break; }
+ }
+ if win_row { return true; }
+ let mut win_col = true;
+ for i in 0..board.shape()[0] {
+ if !board[[i, pivot[1]]].0 { win_col = false; break; }
+ }
+ win_col
+}
+
+fn board_score(board: & Matrix) -> u64 {
+ board.data().into_iter().filter(|x| !x.0).map(|x| x.1).sum()
+}
+
+pub fn main() {
+ let mut stdin = std::io::stdin();
+
+ let numbers: Vec<u64> = {
+ let mut handle = stdin.lock();
+ let mut lines = handle.lines();
+ let line = lines.next().expect("Malformed input").expect("IO error");
+ let mut words = line.split(',');
+ words.map(|s| s.parse().expect("Malformed input")).collect()
+ };
+
+ {
+ let mut handle = stdin.lock();
+ let mut lines = handle.lines();
+ lines.next();
+ }
+ let mut handle = stdin.lock();
+
+ let mut boards: Vec<Matrix> = Vec::new();
+ let mut inverse: HashMap<u64, Vec<(usize, Index)>> = HashMap::new();
+
+ while let Some(mat) = read_board(&mut handle) {
+ let shape = mat.shape();
+ for i in 0..shape[0] {
+ for j in 0..shape[1] {
+ let x = mat[[i, j]].1;
+ if let Some(k) = inverse.get_mut(&x) {
+ k.push((boards.len(), [i, j]));
+ }
+ else { inverse.insert(x, vec![(boards.len(), [i, j])]); }
+ }
+ }
+ boards.push(mat);
+ }
+
+ //println!("Got {} boards and {} inv maps elements.", boards.len(), inverse.len());
+
+ for x in numbers.into_iter() {
+ for (board_num, idx) in inverse.get(&x).unwrap().into_iter() {
+ //println!("Marking ({}, {}) on board {}", idx[0], idx[1], board_num);
+ let board: &mut Matrix = boards.index_mut(*board_num);
+ board[idx].0 = true;
+ if is_board_winning(board, *idx) {
+ println!("{}", board_score(board)*x);
+ return;
+ }
+ }
+ }
+}
diff --git a/04/src/part-2.rs b/04/src/part-2.rs
new file mode 100644
index 0000000..9903375
--- /dev/null
+++ b/04/src/part-2.rs
@@ -0,0 +1,95 @@
+mod tensor;
+
+use std::io::{BufRead};
+use std::collections::{HashMap, HashSet};
+use std::ops::{IndexMut};
+
+pub type Matrix = tensor::Tensor<(bool, u64), 2>;
+pub type Index = tensor::Index<2>;
+
+fn read_board(handle: &mut std::io::StdinLock) -> Option<Matrix> {
+ let mut lines = handle.lines();
+ let first_line = lines.next()?.ok()?;
+ let mut words = first_line.split_whitespace();
+ let mut data: Vec<(bool, u64)> = words.map(|x| (false, x.parse().expect("Malformed input"))).collect();
+ let n = data.len();
+ for i in 1..n {
+ let line = lines.next().expect("Malformed input").expect("IO error");
+ let mut words = line.split_whitespace();
+ data.extend(words.map(|x| (false, x.parse::<u64>().expect("Malformed input"))));
+ }
+ lines.next();
+ Some(Matrix::new_from([n, n], data))
+}
+
+fn is_board_winning(board: & Matrix, pivot: Index) -> bool {
+ let mut win_row = true;
+ for j in 0..board.shape()[1] {
+ if !board[[pivot[0], j]].0 { win_row = false; break; }
+ }
+ if win_row { return true; }
+ let mut win_col = true;
+ for i in 0..board.shape()[0] {
+ if !board[[i, pivot[1]]].0 { win_col = false; break; }
+ }
+ win_col
+}
+
+fn board_score(board: & Matrix) -> u64 {
+ board.data().into_iter().filter(|x| !x.0).map(|x| x.1).sum()
+}
+
+pub fn main() {
+ let mut stdin = std::io::stdin();
+
+ let numbers: Vec<u64> = {
+ let mut handle = stdin.lock();
+ let mut lines = handle.lines();
+ let line = lines.next().expect("Malformed input").expect("IO error");
+ let mut words = line.split(',');
+ words.map(|s| s.parse().expect("Malformed input")).collect()
+ };
+
+ {
+ let mut handle = stdin.lock();
+ let mut lines = handle.lines();
+ lines.next();
+ }
+ let mut handle = stdin.lock();
+
+ let mut boards: Vec<Matrix> = Vec::new();
+ let mut inverse: HashMap<u64, Vec<(usize, Index)>> = HashMap::new();
+
+ while let Some(mat) = read_board(&mut handle) {
+ let shape = mat.shape();
+ for i in 0..shape[0] {
+ for j in 0..shape[1] {
+ let x = mat[[i, j]].1;
+ if let Some(k) = inverse.get_mut(&x) {
+ k.push((boards.len(), [i, j]));
+ }
+ else { inverse.insert(x, vec![(boards.len(), [i, j])]); }
+ }
+ }
+ boards.push(mat);
+ }
+
+ //println!("Got {} boards and {} inv maps elements.", boards.len(), inverse.len());
+ let mut boards_not_yet_won: HashSet<usize> = HashSet::new();
+ boards_not_yet_won.extend(0..boards.len());
+ for x in numbers.into_iter() {
+ for (board_num, idx) in inverse.get(&x).unwrap().into_iter() {
+ //println!("Marking ({}, {}) on board {}", idx[0], idx[1], board_num);
+ let board: &mut Matrix = boards.index_mut(*board_num);
+ board[idx].0 = true;
+ if is_board_winning(board, *idx) {
+ boards_not_yet_won.remove(board_num);
+ if boards_not_yet_won.is_empty() {
+ println!("{}", board_score(board)*x);
+ return;
+ }
+ //winning_boards.push((*board_num, board_score(board)*x));
+ }
+ }
+ }
+}
diff --git a/04/src/tensor.rs b/04/src/tensor.rs
new file mode 100644
index 0000000..6760d99
--- /dev/null
+++ b/04/src/tensor.rs
@@ -0,0 +1,156 @@
+#![allow(dead_code)]
+
+pub type Shape<const D: usize> = [usize; D];
+pub type Index<const D: usize> = Shape<D>;
+
+pub struct Tensor<T, const D: usize> {
+ data: Vec<T>,
+ shape: Shape<D>,
+ strides: Shape<D>
+}
+
+pub type Tensor1<T> = Tensor<T, 1>;
+pub type Tensor2<T> = Tensor<T, 2>;
+pub type Tensor3<T> = Tensor<T, 3>;
+pub type Tensor4<T> = Tensor<T, 4>;
+pub type Index1 = Index<1>;
+pub type Index2 = Index<2>;
+pub type Index3 = Index<3>;
+pub type Index4 = Index<4>;
+
+
+impl<T: Copy, const D: usize> Tensor<T, D> {
+ pub fn new(shape: Shape<D>, x: T) -> Self {
+ let dim = D;
+ if dim == 0 { panic!("Empty shape not allowed."); }
+
+ let mut len = shape[dim-1];
+ let mut strides: Shape<D> = [0; D];
+ strides[dim-1] = 1;
+ for d in (1..dim).rev() { // d=dim-1, …, 1.
+ strides[d-1] = shape[d]*strides[d];
+ len *= shape[d-1];
+ }
+ if len == 0 { panic!("Empty dimensions not allowed."); }
+
+ Self {
+ data: vec![x; len],
+ shape: shape,
+ strides: strides,
+ }
+ }
+
+ pub fn new_from(shape: Shape<D>, x: Vec<T>) -> Self {
+ let dim = D;
+ if dim == 0 { panic!("Empty shape not allowed."); }
+
+ let mut len = shape[dim-1];
+ let mut strides: Shape<D> = [0; D];
+ strides[dim-1] = 1;
+ for d in (1..dim).rev() { // d=dim-1, …, 1.
+ strides[d-1] = shape[d]*strides[d];
+ len *= shape[d-1];
+ }
+ if len == 0 { panic!("Empty dimensions not allowed."); }
+
+ if len != x.len() { panic!("Vector of length {} cannot fill tensor with {} entries.", x.len(), len); }
+
+ Self {
+ data: x,
+ shape: shape,
+ strides: strides,
+ }
+ }
+
+
+ #[inline(always)]
+ fn flatten_idx(self: & Self, idx: & Index<D>) -> usize {
+ // NOTE: This is a very hot code path. Should benchmark versus explicit loop.
+ idx.iter().zip(self.strides.iter()).fold(0, |sum, (i, s)| sum + i*s)
+ }
+
+ fn bound_check_panic(self: & Self, idx: & Index<D>) -> () {
+ for d in 0..self.dim() {
+ let i = *(unsafe { idx.get_unchecked(d) });
+ if i >= self.shape[d] {
+ panic!("{}-dimensional tensor index is out of bounds in dimension {} ({} >= {}).", self.dim(), d, i, self.shape[d])
+ }
+ }
+ }
+
+ pub fn dim(self: & Self) -> usize { D }
+
+ pub fn shape(self: & Self) -> & Shape<D> { &self.shape }
+
+ pub unsafe fn el_unchecked(self: & Self, idx: & Index<D>) -> & T { self.data.get_unchecked(self.flatten_idx(idx)) }
+
+ pub unsafe fn el_unchecked_mut(self: &mut Self, idx: & Index<D>) -> &mut T {
+ let flat_idx = self.flatten_idx(idx);
+ self.data.get_unchecked_mut(flat_idx)
+ }
+
+ pub fn flat_len(self: & Self) -> usize { self.data.len() }
+ pub fn size(self: & Self) -> usize { self.flat_len()*std::mem::size_of::<T>() }
+
+ pub fn fill_with(self: &mut Self, x: & [T]) -> () {
+ // Already panics on size mismatch.
+ self.data.copy_from_slice(x)
+ }
+
+ pub fn data(self: & Self) -> & [T] { &self.data }
+}
+
+impl<T: Copy + std::fmt::Display> Tensor<T, 2> {
+ pub fn dirty_print(self: & Self) {
+ for i in 0..self.shape[0] {
+ for j in 0..self.shape[1] {
+ print!("{} ", self[[i, j]]);
+ }
+ println!("");
+ }
+ }
+}
+
+impl<T: Copy, const D: usize> std::ops::Index<Index<D>> for Tensor<T, D> {
+ type Output = T;
+
+ fn index(self: & Self, idx: Index<D>) -> & Self::Output {
+ self.bound_check_panic(&idx);
+ unsafe { self.el_unchecked(&idx) }
+ }
+}
+
+impl<T: Copy, const D: usize> std::ops::Index<& Index<D>> for Tensor<T, D> {
+ type Output = T;
+
+ fn index(self: & Self, idx: & Index<D>) -> & Self::Output {
+ self.bound_check_panic(idx);
+ unsafe { self.el_unchecked(idx) }
+ }
+}
+
+impl<T: Copy, const D: usize> std::ops::IndexMut<Index<D>> for Tensor<T, D> {
+ fn index_mut(self: &mut Self, idx: Index<D>) -> &mut Self::Output {
+ self.bound_check_panic(&idx);
+ unsafe { self.el_unchecked_mut(&idx) }
+ }
+}
+
+impl<T: Copy, const D: usize> std::ops::IndexMut<& Index<D>> for Tensor<T, D> {
+ fn index_mut(self: &mut Self, idx: & Index<D>) -> &mut Self::Output {
+ self.bound_check_panic(idx);
+ unsafe { self.el_unchecked_mut(idx) }
+ }
+}
+
+impl<T: Copy, const D: usize> IntoIterator for Tensor<T, D> {
+ type Item = T;
+ type IntoIter = std::vec::IntoIter<Self::Item>;
+
+ fn into_iter(self) -> Self::IntoIter { self.data.into_iter() }
+}
+
+
+// FIXME: Should have a proper IntoIter implementing IntoIter for &'a Tensor<T, D>.
+
+// Note: Tensor is also sliceable (due to the Index implementations)
diff --git a/04/test.txt b/04/test.txt
new file mode 100644
index 0000000..f06791d
--- /dev/null
+++ b/04/test.txt
@@ -0,0 +1,20 @@
+7,4,9,5,11,17,23,2,0,14,21,24,10,16,13,6,15,25,12,22,18,20,8,19,3,26,1
+
+22 13 17 11 0
+ 8 2 23 4 24
+21 9 14 16 7
+ 6 10 3 18 5
+ 1 12 20 15 19
+
+ 3 15 0 2 22
+ 9 18 13 17 5
+19 8 7 25 23
+20 11 10 24 4
+14 21 16 12 6
+
+14 21 17 24 4
+10 16 15 9 19
+18 8 23 26 20
+22 11 13 6 5
+ 2 0 12 3 7
+