| author | |
| committer | |
| log | 3b77a845f9a5409d19a9e330b82eece8af4ac18f |
| tree | f19a742742aabef64c6ce3842b3a12b97de52161 |
| parent | 1639fcea43549853f1fded32aa1d711d21771e1c |
by making it always intrusive, we make it more broadly useful API, and
avoid binary bloat.3 files changed, 275 insertions(+), 288 deletions(-)
lib/std/DoublyLinkedList.zig created+274| ... | ... | @@ -0,0 +1,274 @@ |
| 1 | //! A doubly-linked list has a pair of pointers to both the head and | |
| 2 | //! tail of the list. List elements have pointers to both the previous | |
| 3 | //! and next elements in the sequence. The list can be traversed both | |
| 4 | //! forward and backward. Some operations that take linear O(n) time | |
| 5 | //! with a singly-linked list can be done without traversal in constant | |
| 6 | //! O(1) time with a doubly-linked list: | |
| 7 | //! | |
| 8 | //! * Removing an element. | |
| 9 | //! * Inserting a new element before an existing element. | |
| 10 | //! * Pushing or popping an element from the end of the list. | |
| 11 | ||
| 12 | const std = @import("std.zig"); | |
| 13 | const debug = std.debug; | |
| 14 | const assert = debug.assert; | |
| 15 | const testing = std.testing; | |
| 16 | const DoublyLinkedList = @This(); | |
| 17 | ||
| 18 | first: ?*Node = null, | |
| 19 | last: ?*Node = null, | |
| 20 | len: usize = 0, | |
| 21 | ||
| 22 | /// This struct contains only the prev and next pointers and not any data | |
| 23 | /// payload. The intended usage is to embed it intrusively into another data | |
| 24 | /// structure and access the data with `@fieldParentPtr`. | |
| 25 | pub const Node = struct { | |
| 26 | prev: ?*Node = null, | |
| 27 | next: ?*Node = null, | |
| 28 | }; | |
| 29 | ||
| 30 | pub fn insertAfter(list: *DoublyLinkedList, existing_node: *Node, new_node: *Node) void { | |
| 31 | new_node.prev = existing_node; | |
| 32 | if (existing_node.next) |next_node| { | |
| 33 | // Intermediate node. | |
| 34 | new_node.next = next_node; | |
| 35 | next_node.prev = new_node; | |
| 36 | } else { | |
| 37 | // Last element of the list. | |
| 38 | new_node.next = null; | |
| 39 | list.last = new_node; | |
| 40 | } | |
| 41 | existing_node.next = new_node; | |
| 42 | ||
| 43 | list.len += 1; | |
| 44 | } | |
| 45 | ||
| 46 | pub fn insertBefore(list: *DoublyLinkedList, existing_node: *Node, new_node: *Node) void { | |
| 47 | new_node.next = existing_node; | |
| 48 | if (existing_node.prev) |prev_node| { | |
| 49 | // Intermediate node. | |
| 50 | new_node.prev = prev_node; | |
| 51 | prev_node.next = new_node; | |
| 52 | } else { | |
| 53 | // First element of the list. | |
| 54 | new_node.prev = null; | |
| 55 | list.first = new_node; | |
| 56 | } | |
| 57 | existing_node.prev = new_node; | |
| 58 | ||
| 59 | list.len += 1; | |
| 60 | } | |
| 61 | ||
| 62 | /// Concatenate list2 onto the end of list1, removing all entries from the former. | |
| 63 | /// | |
| 64 | /// Arguments: | |
| 65 | /// list1: the list to concatenate onto | |
| 66 | /// list2: the list to be concatenated | |
| 67 | pub fn concatByMoving(list1: *DoublyLinkedList, list2: *DoublyLinkedList) void { | |
| 68 | const l2_first = list2.first orelse return; | |
| 69 | if (list1.last) |l1_last| { | |
| 70 | l1_last.next = list2.first; | |
| 71 | l2_first.prev = list1.last; | |
| 72 | list1.len += list2.len; | |
| 73 | } else { | |
| 74 | // list1 was empty | |
| 75 | list1.first = list2.first; | |
| 76 | list1.len = list2.len; | |
| 77 | } | |
| 78 | list1.last = list2.last; | |
| 79 | list2.first = null; | |
| 80 | list2.last = null; | |
| 81 | list2.len = 0; | |
| 82 | } | |
| 83 | ||
| 84 | /// Insert a new node at the end of the list. | |
| 85 | /// | |
| 86 | /// Arguments: | |
| 87 | /// new_node: Pointer to the new node to insert. | |
| 88 | pub fn append(list: *DoublyLinkedList, new_node: *Node) void { | |
| 89 | if (list.last) |last| { | |
| 90 | // Insert after last. | |
| 91 | list.insertAfter(last, new_node); | |
| 92 | } else { | |
| 93 | // Empty list. | |
| 94 | list.prepend(new_node); | |
| 95 | } | |
| 96 | } | |
| 97 | ||
| 98 | /// Insert a new node at the beginning of the list. | |
| 99 | /// | |
| 100 | /// Arguments: | |
| 101 | /// new_node: Pointer to the new node to insert. | |
| 102 | pub fn prepend(list: *DoublyLinkedList, new_node: *Node) void { | |
| 103 | if (list.first) |first| { | |
| 104 | // Insert before first. | |
| 105 | list.insertBefore(first, new_node); | |
| 106 | } else { | |
| 107 | // Empty list. | |
| 108 | list.first = new_node; | |
| 109 | list.last = new_node; | |
| 110 | new_node.prev = null; | |
| 111 | new_node.next = null; | |
| 112 | ||
| 113 | list.len = 1; | |
| 114 | } | |
| 115 | } | |
| 116 | ||
| 117 | /// Remove a node from the list. | |
| 118 | /// | |
| 119 | /// Arguments: | |
| 120 | /// node: Pointer to the node to be removed. | |
| 121 | pub fn remove(list: *DoublyLinkedList, node: *Node) void { | |
| 122 | if (node.prev) |prev_node| { | |
| 123 | // Intermediate node. | |
| 124 | prev_node.next = node.next; | |
| 125 | } else { | |
| 126 | // First element of the list. | |
| 127 | list.first = node.next; | |
| 128 | } | |
| 129 | ||
| 130 | if (node.next) |next_node| { | |
| 131 | // Intermediate node. | |
| 132 | next_node.prev = node.prev; | |
| 133 | } else { | |
| 134 | // Last element of the list. | |
| 135 | list.last = node.prev; | |
| 136 | } | |
| 137 | ||
| 138 | list.len -= 1; | |
| 139 | assert(list.len == 0 or (list.first != null and list.last != null)); | |
| 140 | } | |
| 141 | ||
| 142 | /// Remove and return the last node in the list. | |
| 143 | /// | |
| 144 | /// Returns: | |
| 145 | /// A pointer to the last node in the list. | |
| 146 | pub fn pop(list: *DoublyLinkedList) ?*Node { | |
| 147 | const last = list.last orelse return null; | |
| 148 | list.remove(last); | |
| 149 | return last; | |
| 150 | } | |
| 151 | ||
| 152 | /// Remove and return the first node in the list. | |
| 153 | /// | |
| 154 | /// Returns: | |
| 155 | /// A pointer to the first node in the list. | |
| 156 | pub fn popFirst(list: *DoublyLinkedList) ?*Node { | |
| 157 | const first = list.first orelse return null; | |
| 158 | list.remove(first); | |
| 159 | return first; | |
| 160 | } | |
| 161 | ||
| 162 | test "basic DoublyLinkedList test" { | |
| 163 | const L = DoublyLinkedList(u32); | |
| 164 | var list = L{}; | |
| 165 | ||
| 166 | var one = L.Node{ .data = 1 }; | |
| 167 | var two = L.Node{ .data = 2 }; | |
| 168 | var three = L.Node{ .data = 3 }; | |
| 169 | var four = L.Node{ .data = 4 }; | |
| 170 | var five = L.Node{ .data = 5 }; | |
| 171 | ||
| 172 | list.append(&two); // {2} | |
| 173 | list.append(&five); // {2, 5} | |
| 174 | list.prepend(&one); // {1, 2, 5} | |
| 175 | list.insertBefore(&five, &four); // {1, 2, 4, 5} | |
| 176 | list.insertAfter(&two, &three); // {1, 2, 3, 4, 5} | |
| 177 | ||
| 178 | // Traverse forwards. | |
| 179 | { | |
| 180 | var it = list.first; | |
| 181 | var index: u32 = 1; | |
| 182 | while (it) |node| : (it = node.next) { | |
| 183 | try testing.expect(node.data == index); | |
| 184 | index += 1; | |
| 185 | } | |
| 186 | } | |
| 187 | ||
| 188 | // Traverse backwards. | |
| 189 | { | |
| 190 | var it = list.last; | |
| 191 | var index: u32 = 1; | |
| 192 | while (it) |node| : (it = node.prev) { | |
| 193 | try testing.expect(node.data == (6 - index)); | |
| 194 | index += 1; | |
| 195 | } | |
| 196 | } | |
| 197 | ||
| 198 | _ = list.popFirst(); // {2, 3, 4, 5} | |
| 199 | _ = list.pop(); // {2, 3, 4} | |
| 200 | list.remove(&three); // {2, 4} | |
| 201 | ||
| 202 | try testing.expect(list.first.?.data == 2); | |
| 203 | try testing.expect(list.last.?.data == 4); | |
| 204 | try testing.expect(list.len == 2); | |
| 205 | } | |
| 206 | ||
| 207 | test "DoublyLinkedList concatenation" { | |
| 208 | const L = DoublyLinkedList(u32); | |
| 209 | var list1 = L{}; | |
| 210 | var list2 = L{}; | |
| 211 | ||
| 212 | var one = L.Node{ .data = 1 }; | |
| 213 | var two = L.Node{ .data = 2 }; | |
| 214 | var three = L.Node{ .data = 3 }; | |
| 215 | var four = L.Node{ .data = 4 }; | |
| 216 | var five = L.Node{ .data = 5 }; | |
| 217 | ||
| 218 | list1.append(&one); | |
| 219 | list1.append(&two); | |
| 220 | list2.append(&three); | |
| 221 | list2.append(&four); | |
| 222 | list2.append(&five); | |
| 223 | ||
| 224 | list1.concatByMoving(&list2); | |
| 225 | ||
| 226 | try testing.expect(list1.last == &five); | |
| 227 | try testing.expect(list1.len == 5); | |
| 228 | try testing.expect(list2.first == null); | |
| 229 | try testing.expect(list2.last == null); | |
| 230 | try testing.expect(list2.len == 0); | |
| 231 | ||
| 232 | // Traverse forwards. | |
| 233 | { | |
| 234 | var it = list1.first; | |
| 235 | var index: u32 = 1; | |
| 236 | while (it) |node| : (it = node.next) { | |
| 237 | try testing.expect(node.data == index); | |
| 238 | index += 1; | |
| 239 | } | |
| 240 | } | |
| 241 | ||
| 242 | // Traverse backwards. | |
| 243 | { | |
| 244 | var it = list1.last; | |
| 245 | var index: u32 = 1; | |
| 246 | while (it) |node| : (it = node.prev) { | |
| 247 | try testing.expect(node.data == (6 - index)); | |
| 248 | index += 1; | |
| 249 | } | |
| 250 | } | |
| 251 | ||
| 252 | // Swap them back, this verifies that concatenating to an empty list works. | |
| 253 | list2.concatByMoving(&list1); | |
| 254 | ||
| 255 | // Traverse forwards. | |
| 256 | { | |
| 257 | var it = list2.first; | |
| 258 | var index: u32 = 1; | |
| 259 | while (it) |node| : (it = node.next) { | |
| 260 | try testing.expect(node.data == index); | |
| 261 | index += 1; | |
| 262 | } | |
| 263 | } | |
| 264 | ||
| 265 | // Traverse backwards. | |
| 266 | { | |
| 267 | var it = list2.last; | |
| 268 | var index: u32 = 1; | |
| 269 | while (it) |node| : (it = node.prev) { | |
| 270 | try testing.expect(node.data == (6 - index)); | |
| 271 | index += 1; | |
| 272 | } | |
| 273 | } | |
| 274 | } |
lib/std/linked_list.zig deleted-287| ... | ... | @@ -1,287 +0,0 @@ |
| 1 | const std = @import("std.zig"); | |
| 2 | const debug = std.debug; | |
| 3 | const assert = debug.assert; | |
| 4 | const testing = std.testing; | |
| 5 | ||
| 6 | /// A doubly-linked list has a pair of pointers to both the head and | |
| 7 | /// tail of the list. List elements have pointers to both the previous | |
| 8 | /// and next elements in the sequence. The list can be traversed both | |
| 9 | /// forward and backward. Some operations that take linear O(n) time | |
| 10 | /// with a singly-linked list can be done without traversal in constant | |
| 11 | /// O(1) time with a doubly-linked list: | |
| 12 | /// | |
| 13 | /// - Removing an element. | |
| 14 | /// - Inserting a new element before an existing element. | |
| 15 | /// - Pushing or popping an element from the end of the list. | |
| 16 | pub fn DoublyLinkedList(comptime T: type) type { | |
| 17 | return struct { | |
| 18 | const Self = @This(); | |
| 19 | ||
| 20 | /// Node inside the linked list wrapping the actual data. | |
| 21 | pub const Node = struct { | |
| 22 | prev: ?*Node = null, | |
| 23 | next: ?*Node = null, | |
| 24 | data: T, | |
| 25 | }; | |
| 26 | ||
| 27 | first: ?*Node = null, | |
| 28 | last: ?*Node = null, | |
| 29 | len: usize = 0, | |
| 30 | ||
| 31 | /// Insert a new node after an existing one. | |
| 32 | /// | |
| 33 | /// Arguments: | |
| 34 | /// node: Pointer to a node in the list. | |
| 35 | /// new_node: Pointer to the new node to insert. | |
| 36 | pub fn insertAfter(list: *Self, node: *Node, new_node: *Node) void { | |
| 37 | new_node.prev = node; | |
| 38 | if (node.next) |next_node| { | |
| 39 | // Intermediate node. | |
| 40 | new_node.next = next_node; | |
| 41 | next_node.prev = new_node; | |
| 42 | } else { | |
| 43 | // Last element of the list. | |
| 44 | new_node.next = null; | |
| 45 | list.last = new_node; | |
| 46 | } | |
| 47 | node.next = new_node; | |
| 48 | ||
| 49 | list.len += 1; | |
| 50 | } | |
| 51 | ||
| 52 | /// Insert a new node before an existing one. | |
| 53 | /// | |
| 54 | /// Arguments: | |
| 55 | /// node: Pointer to a node in the list. | |
| 56 | /// new_node: Pointer to the new node to insert. | |
| 57 | pub fn insertBefore(list: *Self, node: *Node, new_node: *Node) void { | |
| 58 | new_node.next = node; | |
| 59 | if (node.prev) |prev_node| { | |
| 60 | // Intermediate node. | |
| 61 | new_node.prev = prev_node; | |
| 62 | prev_node.next = new_node; | |
| 63 | } else { | |
| 64 | // First element of the list. | |
| 65 | new_node.prev = null; | |
| 66 | list.first = new_node; | |
| 67 | } | |
| 68 | node.prev = new_node; | |
| 69 | ||
| 70 | list.len += 1; | |
| 71 | } | |
| 72 | ||
| 73 | /// Concatenate list2 onto the end of list1, removing all entries from the former. | |
| 74 | /// | |
| 75 | /// Arguments: | |
| 76 | /// list1: the list to concatenate onto | |
| 77 | /// list2: the list to be concatenated | |
| 78 | pub fn concatByMoving(list1: *Self, list2: *Self) void { | |
| 79 | const l2_first = list2.first orelse return; | |
| 80 | if (list1.last) |l1_last| { | |
| 81 | l1_last.next = list2.first; | |
| 82 | l2_first.prev = list1.last; | |
| 83 | list1.len += list2.len; | |
| 84 | } else { | |
| 85 | // list1 was empty | |
| 86 | list1.first = list2.first; | |
| 87 | list1.len = list2.len; | |
| 88 | } | |
| 89 | list1.last = list2.last; | |
| 90 | list2.first = null; | |
| 91 | list2.last = null; | |
| 92 | list2.len = 0; | |
| 93 | } | |
| 94 | ||
| 95 | /// Insert a new node at the end of the list. | |
| 96 | /// | |
| 97 | /// Arguments: | |
| 98 | /// new_node: Pointer to the new node to insert. | |
| 99 | pub fn append(list: *Self, new_node: *Node) void { | |
| 100 | if (list.last) |last| { | |
| 101 | // Insert after last. | |
| 102 | list.insertAfter(last, new_node); | |
| 103 | } else { | |
| 104 | // Empty list. | |
| 105 | list.prepend(new_node); | |
| 106 | } | |
| 107 | } | |
| 108 | ||
| 109 | /// Insert a new node at the beginning of the list. | |
| 110 | /// | |
| 111 | /// Arguments: | |
| 112 | /// new_node: Pointer to the new node to insert. | |
| 113 | pub fn prepend(list: *Self, new_node: *Node) void { | |
| 114 | if (list.first) |first| { | |
| 115 | // Insert before first. | |
| 116 | list.insertBefore(first, new_node); | |
| 117 | } else { | |
| 118 | // Empty list. | |
| 119 | list.first = new_node; | |
| 120 | list.last = new_node; | |
| 121 | new_node.prev = null; | |
| 122 | new_node.next = null; | |
| 123 | ||
| 124 | list.len = 1; | |
| 125 | } | |
| 126 | } | |
| 127 | ||
| 128 | /// Remove a node from the list. | |
| 129 | /// | |
| 130 | /// Arguments: | |
| 131 | /// node: Pointer to the node to be removed. | |
| 132 | pub fn remove(list: *Self, node: *Node) void { | |
| 133 | if (node.prev) |prev_node| { | |
| 134 | // Intermediate node. | |
| 135 | prev_node.next = node.next; | |
| 136 | } else { | |
| 137 | // First element of the list. | |
| 138 | list.first = node.next; | |
| 139 | } | |
| 140 | ||
| 141 | if (node.next) |next_node| { | |
| 142 | // Intermediate node. | |
| 143 | next_node.prev = node.prev; | |
| 144 | } else { | |
| 145 | // Last element of the list. | |
| 146 | list.last = node.prev; | |
| 147 | } | |
| 148 | ||
| 149 | list.len -= 1; | |
| 150 | assert(list.len == 0 or (list.first != null and list.last != null)); | |
| 151 | } | |
| 152 | ||
| 153 | /// Remove and return the last node in the list. | |
| 154 | /// | |
| 155 | /// Returns: | |
| 156 | /// A pointer to the last node in the list. | |
| 157 | pub fn pop(list: *Self) ?*Node { | |
| 158 | const last = list.last orelse return null; | |
| 159 | list.remove(last); | |
| 160 | return last; | |
| 161 | } | |
| 162 | ||
| 163 | /// Remove and return the first node in the list. | |
| 164 | /// | |
| 165 | /// Returns: | |
| 166 | /// A pointer to the first node in the list. | |
| 167 | pub fn popFirst(list: *Self) ?*Node { | |
| 168 | const first = list.first orelse return null; | |
| 169 | list.remove(first); | |
| 170 | return first; | |
| 171 | } | |
| 172 | }; | |
| 173 | } | |
| 174 | ||
| 175 | test "basic DoublyLinkedList test" { | |
| 176 | const L = DoublyLinkedList(u32); | |
| 177 | var list = L{}; | |
| 178 | ||
| 179 | var one = L.Node{ .data = 1 }; | |
| 180 | var two = L.Node{ .data = 2 }; | |
| 181 | var three = L.Node{ .data = 3 }; | |
| 182 | var four = L.Node{ .data = 4 }; | |
| 183 | var five = L.Node{ .data = 5 }; | |
| 184 | ||
| 185 | list.append(&two); // {2} | |
| 186 | list.append(&five); // {2, 5} | |
| 187 | list.prepend(&one); // {1, 2, 5} | |
| 188 | list.insertBefore(&five, &four); // {1, 2, 4, 5} | |
| 189 | list.insertAfter(&two, &three); // {1, 2, 3, 4, 5} | |
| 190 | ||
| 191 | // Traverse forwards. | |
| 192 | { | |
| 193 | var it = list.first; | |
| 194 | var index: u32 = 1; | |
| 195 | while (it) |node| : (it = node.next) { | |
| 196 | try testing.expect(node.data == index); | |
| 197 | index += 1; | |
| 198 | } | |
| 199 | } | |
| 200 | ||
| 201 | // Traverse backwards. | |
| 202 | { | |
| 203 | var it = list.last; | |
| 204 | var index: u32 = 1; | |
| 205 | while (it) |node| : (it = node.prev) { | |
| 206 | try testing.expect(node.data == (6 - index)); | |
| 207 | index += 1; | |
| 208 | } | |
| 209 | } | |
| 210 | ||
| 211 | _ = list.popFirst(); // {2, 3, 4, 5} | |
| 212 | _ = list.pop(); // {2, 3, 4} | |
| 213 | list.remove(&three); // {2, 4} | |
| 214 | ||
| 215 | try testing.expect(list.first.?.data == 2); | |
| 216 | try testing.expect(list.last.?.data == 4); | |
| 217 | try testing.expect(list.len == 2); | |
| 218 | } | |
| 219 | ||
| 220 | test "DoublyLinkedList concatenation" { | |
| 221 | const L = DoublyLinkedList(u32); | |
| 222 | var list1 = L{}; | |
| 223 | var list2 = L{}; | |
| 224 | ||
| 225 | var one = L.Node{ .data = 1 }; | |
| 226 | var two = L.Node{ .data = 2 }; | |
| 227 | var three = L.Node{ .data = 3 }; | |
| 228 | var four = L.Node{ .data = 4 }; | |
| 229 | var five = L.Node{ .data = 5 }; | |
| 230 | ||
| 231 | list1.append(&one); | |
| 232 | list1.append(&two); | |
| 233 | list2.append(&three); | |
| 234 | list2.append(&four); | |
| 235 | list2.append(&five); | |
| 236 | ||
| 237 | list1.concatByMoving(&list2); | |
| 238 | ||
| 239 | try testing.expect(list1.last == &five); | |
| 240 | try testing.expect(list1.len == 5); | |
| 241 | try testing.expect(list2.first == null); | |
| 242 | try testing.expect(list2.last == null); | |
| 243 | try testing.expect(list2.len == 0); | |
| 244 | ||
| 245 | // Traverse forwards. | |
| 246 | { | |
| 247 | var it = list1.first; | |
| 248 | var index: u32 = 1; | |
| 249 | while (it) |node| : (it = node.next) { | |
| 250 | try testing.expect(node.data == index); | |
| 251 | index += 1; | |
| 252 | } | |
| 253 | } | |
| 254 | ||
| 255 | // Traverse backwards. | |
| 256 | { | |
| 257 | var it = list1.last; | |
| 258 | var index: u32 = 1; | |
| 259 | while (it) |node| : (it = node.prev) { | |
| 260 | try testing.expect(node.data == (6 - index)); | |
| 261 | index += 1; | |
| 262 | } | |
| 263 | } | |
| 264 | ||
| 265 | // Swap them back, this verifies that concatenating to an empty list works. | |
| 266 | list2.concatByMoving(&list1); | |
| 267 | ||
| 268 | // Traverse forwards. | |
| 269 | { | |
| 270 | var it = list2.first; | |
| 271 | var index: u32 = 1; | |
| 272 | while (it) |node| : (it = node.next) { | |
| 273 | try testing.expect(node.data == index); | |
| 274 | index += 1; | |
| 275 | } | |
| 276 | } | |
| 277 | ||
| 278 | // Traverse backwards. | |
| 279 | { | |
| 280 | var it = list2.last; | |
| 281 | var index: u32 = 1; | |
| 282 | while (it) |node| : (it = node.prev) { | |
| 283 | try testing.expect(node.data == (6 - index)); | |
| 284 | index += 1; | |
| 285 | } | |
| 286 | } | |
| 287 | } |
lib/std/std.zig+1-1| ... | ... | @@ -16,7 +16,7 @@ pub const BufMap = @import("buf_map.zig").BufMap; |
| 16 | 16 | pub const BufSet = @import("buf_set.zig").BufSet; |
| 17 | 17 | pub const StaticStringMap = static_string_map.StaticStringMap; |
| 18 | 18 | pub const StaticStringMapWithEql = static_string_map.StaticStringMapWithEql; |
| 19 | pub const DoublyLinkedList = @import("linked_list.zig").DoublyLinkedList; | |
| 19 | pub const DoublyLinkedList = @import("DoublyLinkedList.zig"); | |
| 20 | 20 | pub const DynLib = @import("dynamic_library.zig").DynLib; |
| 21 | 21 | pub const DynamicBitSet = bit_set.DynamicBitSet; |
| 22 | 22 | pub const DynamicBitSetUnmanaged = bit_set.DynamicBitSetUnmanaged; |