moved code around so each year is a package.
This commit is contained in:
parent
8aa7fe572f
commit
46b91dae30
8
2015/Cargo.toml
Normal file
8
2015/Cargo.toml
Normal file
@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name="aoc2015"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
core = { path = "../core" }
|
||||
md-5 = { workspace = true }
|
||||
@ -22,7 +22,7 @@
|
||||
// To what floor do the instructions take Santa?
|
||||
//
|
||||
|
||||
use aoc::read_data;
|
||||
use core::read_data;
|
||||
|
||||
fn main() {
|
||||
let instructions = read_data("2015_01_data.txt");
|
||||
@ -7,8 +7,7 @@
|
||||
// What is the position of the character that causes Santa to first enter the basement?
|
||||
//
|
||||
|
||||
use aoc::read_data;
|
||||
|
||||
use core::read_data;
|
||||
fn main() {
|
||||
let instructions = read_data("2015_01_data.txt");
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
// they order?
|
||||
//
|
||||
|
||||
use aoc::{read_data, smallest_of_vec, string_to_3u32, sum_of_vec};
|
||||
use core::{read_data, smallest_of_vec, string_to_3u32, sum_of_vec};
|
||||
|
||||
fn calculate_wrapping_needed(length: u32, width: u32, height: u32) -> u32 {
|
||||
let sides = vec![
|
||||
@ -15,7 +15,7 @@
|
||||
// How many total feet of ribbon should they order?
|
||||
//
|
||||
|
||||
use aoc::{read_data, string_to_3u32};
|
||||
use core::{read_data, string_to_3u32};
|
||||
|
||||
fn bow_ribbon_length(l: u32, w: u32, h: u32) -> u32 {
|
||||
l * w * h
|
||||
@ -35,7 +35,8 @@ fn main() {
|
||||
println!("Box sized {} requires {}", "2x3x4", ribbon_for_package(2,3,4));
|
||||
println!("Box sized {} requires {}", "1x1x10", ribbon_for_package(1,1,10));
|
||||
|
||||
let sizes = read_data("2015_02_data.txt").lines();
|
||||
let binding = read_data("2015_02_data.txt");
|
||||
let sizes = binding.lines();
|
||||
let mut total_ribbon = 0;
|
||||
|
||||
for size in sizes {
|
||||
@ -17,7 +17,7 @@
|
||||
// ^v^v^v^v^v delivers a bunch of presents to some very lucky children at only 2 houses.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use aoc::read_data;
|
||||
use core::read_data;
|
||||
|
||||
fn main() {
|
||||
let mut visits: HashMap<String, u32> = HashMap::from([("0x0".to_string(), 1)]);
|
||||
@ -27,19 +27,19 @@ fn main() {
|
||||
for current_instruction in instructions.chars() {
|
||||
match current_instruction {
|
||||
'^' => {
|
||||
println!("Move North 1");
|
||||
//println!("Move North 1");
|
||||
current_y += 1;
|
||||
},
|
||||
'v' => {
|
||||
println!("Move South 1");
|
||||
//println!("Move South 1");
|
||||
current_y -= 1;
|
||||
},
|
||||
'<' => {
|
||||
println!("Move West 1");
|
||||
//println!("Move West 1");
|
||||
current_x -= 1;
|
||||
},
|
||||
'>' => {
|
||||
println!("Move East 1");
|
||||
//println!("Move East 1");
|
||||
current_x += 1;
|
||||
},
|
||||
_ => { unreachable!("Invalid instruction -> {}", ¤t_instruction); }
|
||||
@ -49,7 +49,5 @@ fn main() {
|
||||
}
|
||||
|
||||
println!("Found {} houses got presents.", visits.keys().count());
|
||||
|
||||
println!("Taxation is theft.");
|
||||
}
|
||||
// 2081
|
||||
@ -15,7 +15,7 @@
|
||||
// going the other.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use aoc::read_data;
|
||||
use core::read_data;
|
||||
|
||||
fn num_houses(directions: &str) -> u32 {
|
||||
let (mut santa_x, mut santa_y, mut robo_x, mut robo_y) = (0,0,0,0);
|
||||
@ -59,7 +59,7 @@ fn num_houses(directions: &str) -> u32 {
|
||||
} else {
|
||||
format!("{}x{}", robo_x, robo_y)
|
||||
};
|
||||
println!("V:{} S:{}x{} R:{}x{} V#:{}", current_address, santa_x, santa_y, robo_x, robo_y, visits.keys().count());
|
||||
//println!("V:{} S:{}x{} R:{}x{} V#:{}", current_address, santa_x, santa_y, robo_x, robo_y, visits.keys().count());
|
||||
*visits.entry(current_address).and_modify(|x| *x += 1).or_insert(1);
|
||||
santas_turn = !santas_turn;
|
||||
}
|
||||
@ -42,3 +42,4 @@ fn main() {
|
||||
}
|
||||
}
|
||||
}
|
||||
// 254575
|
||||
@ -30,3 +30,4 @@ fn main() {
|
||||
}
|
||||
}
|
||||
}
|
||||
// 1048970
|
||||
@ -19,7 +19,7 @@
|
||||
// How many strings are nice?
|
||||
//
|
||||
|
||||
use aoc::read_data;
|
||||
use core::read_data;
|
||||
|
||||
fn no_banned_parts(to_check: &str) -> bool {
|
||||
!to_check.contains("ab") &&
|
||||
@ -20,32 +20,30 @@
|
||||
// ieodomkazucvgmuy is naughty because it has a repeating letter with one between (odo), but no
|
||||
// pair that appears twice.
|
||||
|
||||
fn appears_twice_without_overlapping(to_check: &str) -> bool {
|
||||
let mut to_find = vec![];
|
||||
for index in 0..=to_check.len() - 1 {
|
||||
let next_pair = to_check.chars().enumerate();
|
||||
println!("Index {index} = {:?}", next_pair);
|
||||
}
|
||||
use core::read_data;
|
||||
|
||||
true
|
||||
|
||||
fn appears_twice_without_overlapping(to_check: &str) -> bool {
|
||||
let check_chars: Vec<_> = to_check.chars().collect();
|
||||
for index in 0.. check_chars.len() - 1 {
|
||||
let pair = (check_chars[index], check_chars[index + 1]);
|
||||
for subindex in index + 2 .. check_chars.len() - 1 {
|
||||
if check_chars[subindex] == pair.0 && check_chars[subindex + 1] == pair.1 {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn letter_repeated_seperated_by_1(to_check: &str) -> bool {
|
||||
println!("Starting to check for XyX");
|
||||
let mut has_criteria = false;
|
||||
|
||||
let mut two_back = ' ';
|
||||
let mut one_back = ' ';
|
||||
for (index, current) in to_check.chars().enumerate() {
|
||||
if current == two_back {
|
||||
has_criteria = true;
|
||||
println!("Found a seperated pair. {index} / {to_check}");
|
||||
break;
|
||||
let chars: Vec<_> = to_check.chars().collect();
|
||||
for index in 0..chars.len() - 2 {
|
||||
if chars[index] == chars[index + 2] {
|
||||
return true
|
||||
}
|
||||
two_back = one_back;
|
||||
one_back = current;
|
||||
}
|
||||
has_criteria
|
||||
false
|
||||
}
|
||||
|
||||
fn is_nice(to_check: &str) -> bool {
|
||||
@ -58,4 +56,16 @@ fn main() {
|
||||
println!("xxyxx is nice -> {}", is_nice("xxyxx"));
|
||||
println!("uurcxstgmygtbstg is NOT nice -> {}", is_nice("uurcxstgmygtbstg"));
|
||||
println!("ieodomkazucvgmuy is NOT nice -> {}", is_nice("ieodomkazucvgmuy"));
|
||||
|
||||
let binding = read_data("2015_05_data.txt");
|
||||
let lines = binding.lines();
|
||||
let mut num_lines = 0;
|
||||
|
||||
for line in lines {
|
||||
if is_nice(line) {
|
||||
num_lines += 1;
|
||||
}
|
||||
}
|
||||
println!("Found {num_lines} good passwords.");
|
||||
|
||||
}
|
||||
@ -21,7 +21,7 @@
|
||||
// turn off 499,499 through 500,500 would turn off (or leave off) the middle four lights.
|
||||
// After following the instructions, how many lights are lit?
|
||||
|
||||
use aoc::read_data;
|
||||
use core::read_data;
|
||||
|
||||
fn main() {
|
||||
let mut block: [bool; 1000*1000] = [false; 1000*1000];
|
||||
@ -18,7 +18,7 @@
|
||||
// turn on 0,0 through 0,0 would increase the total brightness by 1.
|
||||
// toggle 0,0 through 999,999 would increase the total brightness by 2000000.
|
||||
|
||||
use aoc::read_data;
|
||||
use core::read_data;
|
||||
|
||||
fn main() {
|
||||
let mut block: Box<[u32; 1000*1000]> = Box::new([0; 1000*1000]);
|
||||
@ -1,4 +1,4 @@
|
||||
use aoc::read_data;
|
||||
use core::read_data;
|
||||
|
||||
fn main() {
|
||||
let binding = read_data("2015_08_data.txt");
|
||||
8
2016/Cargo.toml
Normal file
8
2016/Cargo.toml
Normal file
@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name="aoc2016"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
core = { path = "../core" }
|
||||
md-5 = { workspace = true }
|
||||
@ -27,7 +27,7 @@
|
||||
//
|
||||
|
||||
use std::{env, fs};
|
||||
use aoc::read_data;
|
||||
use core::read_data;
|
||||
use crate::CardinalDirection::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -28,7 +28,7 @@
|
||||
|
||||
use std::{env, fs};
|
||||
use std::collections::HashMap;
|
||||
use aoc::read_data;
|
||||
use core::read_data;
|
||||
use crate::CardinalDirection::*;
|
||||
|
||||
fn manhattan_distance(directions: &str) -> i32 {
|
||||
@ -32,7 +32,7 @@
|
||||
// Your puzzle input is the instructions from the document you found at the front desk. What is the bathroom code?
|
||||
//
|
||||
|
||||
use aoc::read_data;
|
||||
use core::read_data;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Location {
|
||||
@ -12,7 +12,7 @@
|
||||
// example, the "triangle" given above is impossible, because 5 + 10 is not larger than 25.
|
||||
//
|
||||
|
||||
use aoc::read_data;
|
||||
use core::read_data;
|
||||
|
||||
// the total of the sides minus the largest side is greater then the longest side
|
||||
fn possibly_valid_triangle(a: u32, b: u32, c: u32) -> bool {
|
||||
@ -1,4 +1,4 @@
|
||||
use aoc::read_data;
|
||||
use core::read_data;
|
||||
|
||||
// the total of the sides minus the largest side is greater then the longest side
|
||||
fn possibly_valid_triangle(a: u32, b: u32, c: u32) -> bool {
|
||||
@ -1,5 +1,5 @@
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use aoc::read_data;
|
||||
use core::read_data;
|
||||
|
||||
struct EncryptedRoom {
|
||||
encrypted_name: String,
|
||||
@ -32,7 +32,7 @@
|
||||
//
|
||||
|
||||
use std::collections::HashMap;
|
||||
use aoc::read_data;
|
||||
use core::read_data;
|
||||
|
||||
fn main() {
|
||||
let binding = read_data("2016_06_data.txt");
|
||||
@ -15,7 +15,7 @@
|
||||
//
|
||||
|
||||
use std::collections::HashMap;
|
||||
use aoc::read_data;
|
||||
use core::read_data;
|
||||
|
||||
fn main() {
|
||||
let binding = read_data("2016_06_data.txt");
|
||||
7
2017/Cargo.toml
Normal file
7
2017/Cargo.toml
Normal file
@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name="aoc2017"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
core = { path = "../core" }
|
||||
@ -7,7 +7,7 @@
|
||||
// 1234 produces 0 because no digit matches the next.
|
||||
// 91212129 produces 9 because the only digit that matches the next one is the last digit, 9.
|
||||
|
||||
use aoc::read_data;
|
||||
use core::read_data;
|
||||
|
||||
fn circular_digit_sum(input: &str) -> u32 {
|
||||
let mut running_total = 0;
|
||||
@ -11,7 +11,7 @@
|
||||
// 123123 produces 12.
|
||||
// 12131415 produces 4.
|
||||
|
||||
use aoc::read_data;
|
||||
use core::read_data;
|
||||
|
||||
fn halfway_sum_thing(input: &str) -> u32 {
|
||||
let mut running_total = 0;
|
||||
@ -1,4 +1,4 @@
|
||||
use aoc::read_data;
|
||||
use core::read_data;
|
||||
|
||||
fn main() {
|
||||
let mut running_total = 0;
|
||||
@ -9,7 +9,7 @@
|
||||
//
|
||||
|
||||
use std::collections::HashMap;
|
||||
use aoc::read_data;
|
||||
use core::read_data;
|
||||
|
||||
fn is_valid(input: &str) -> bool {
|
||||
let parts = input.split(' ');
|
||||
7
2018/Cargo.toml
Normal file
7
2018/Cargo.toml
Normal file
@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name="aoc2018"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
core = { path = "../core" }
|
||||
@ -2,7 +2,7 @@
|
||||
// frequency have been applied?
|
||||
|
||||
use std::collections::HashMap;
|
||||
use aoc::read_data;
|
||||
use core::read_data;
|
||||
|
||||
fn main() {
|
||||
let mut running_total = 0i32;
|
||||
@ -1,7 +1,7 @@
|
||||
// do part 1a but figure out the first repeated value
|
||||
|
||||
use std::collections::HashMap;
|
||||
use aoc::read_data;
|
||||
use core::read_data;
|
||||
|
||||
fn main() {
|
||||
let mut visited_locations: HashMap<i32, i32> = HashMap::from([(0, 0)]);
|
||||
7
2019/Cargo.toml
Normal file
7
2019/Cargo.toml
Normal file
@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name="aoc2019"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
core = { path = "../core" }
|
||||
@ -1,6 +1,6 @@
|
||||
#![feature(int_roundings)]
|
||||
|
||||
use aoc::read_data;
|
||||
use core::read_data;
|
||||
|
||||
fn fuel_calc(input: u32) -> u32 {
|
||||
input.div_floor(3).saturating_sub(2)
|
||||
@ -1,6 +1,6 @@
|
||||
#![feature(int_roundings)]
|
||||
|
||||
use aoc::read_data;
|
||||
use core::read_data;
|
||||
|
||||
fn fuel_calc(input: u32) -> u32 {
|
||||
input.div_floor(3).saturating_sub(2)
|
||||
@ -1,4 +1,4 @@
|
||||
use aoc::read_data;
|
||||
use core::read_data;
|
||||
|
||||
fn main() {
|
||||
let directions = read_data("2019_02_data.txt").split(", ");
|
||||
7
2020/Cargo.toml
Normal file
7
2020/Cargo.toml
Normal file
@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name="aoc2020"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
core = { path = "../core" }
|
||||
@ -1,4 +1,4 @@
|
||||
use aoc::read_data;
|
||||
use core::read_data;
|
||||
|
||||
fn main() {
|
||||
let binding = read_data("2020_01_data.txt");
|
||||
@ -1,4 +1,4 @@
|
||||
use aoc::read_data;
|
||||
use core::read_data;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct ParsedLine {
|
||||
@ -1,5 +1,5 @@
|
||||
use std::io::{stdout, Write};
|
||||
use aoc::read_data;
|
||||
use core::read_data;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct ParsedLine {
|
||||
7
2021/Cargo.toml
Normal file
7
2021/Cargo.toml
Normal file
@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name="aoc2021"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
core = { path = "../core" }
|
||||
@ -1,4 +1,4 @@
|
||||
use aoc::read_data;
|
||||
use core::read_data;
|
||||
|
||||
fn dips(input: &str) -> u32 {
|
||||
let mut last_reading = 0;
|
||||
@ -1,4 +1,4 @@
|
||||
use aoc::read_data;
|
||||
use core::read_data;
|
||||
|
||||
enum CardinalDirection {
|
||||
North,
|
||||
7
2022/Cargo.toml
Normal file
7
2022/Cargo.toml
Normal file
@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name="aoc2022"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
core = { path = "../core" }
|
||||
@ -1,4 +1,4 @@
|
||||
use aoc::read_data;
|
||||
use core::read_data;
|
||||
|
||||
fn main() {
|
||||
let binding = read_data("2022_01_data.txt");
|
||||
@ -1,4 +1,4 @@
|
||||
use aoc::read_data;
|
||||
use core::read_data;
|
||||
|
||||
fn main() {
|
||||
let binding = read_data("2022_01_data.txt");
|
||||
@ -1,4 +1,4 @@
|
||||
use aoc::read_data;
|
||||
use core::read_data;
|
||||
use crate::RPSOutcome::{Loss, Tie, Win};
|
||||
use crate::RPSPlays::{Paper, Rock, Scissors};
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use aoc::read_data;
|
||||
use core::read_data;
|
||||
use crate::RPSOutcome::{Loss, Tie, Win};
|
||||
use crate::RPSPlays::{Paper, Rock, Scissors};
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use std::collections::HashSet;
|
||||
use std::io::BufRead;
|
||||
use aoc::read_data;
|
||||
use core::read_data;
|
||||
|
||||
|
||||
fn char_to_value(input: char) -> u32 {
|
||||
@ -1,4 +1,4 @@
|
||||
use aoc::read_data;
|
||||
use core::read_data;
|
||||
|
||||
fn main() {
|
||||
let binding = read_data("2022_03_data.txt");
|
||||
7
2023/Cargo.toml
Normal file
7
2023/Cargo.toml
Normal file
@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name="aoc2023"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
core = { path = "../core" }
|
||||
@ -1,4 +1,4 @@
|
||||
use aoc::read_data;
|
||||
use core::read_data;
|
||||
|
||||
fn main() {
|
||||
let mut running_total = 0;
|
||||
7
2024/Cargo.toml
Normal file
7
2024/Cargo.toml
Normal file
@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name="aoc2024"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
core = { path = "../core" }
|
||||
71
Cargo.lock
generated
71
Cargo.lock
generated
@ -3,12 +3,77 @@
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "aoc"
|
||||
name = "aoc2015"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"core",
|
||||
"md-5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aoc2016"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"core",
|
||||
"md-5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aoc2017"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aoc2018"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aoc2019"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aoc2020"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aoc2021"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aoc2022"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aoc2023"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aoc2024"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
@ -24,6 +89,10 @@ version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
|
||||
|
||||
[[package]]
|
||||
name = "core"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
|
||||
23
Cargo.toml
23
Cargo.toml
@ -1,7 +1,18 @@
|
||||
[package]
|
||||
name="aoc"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
[workspace]
|
||||
members = [
|
||||
"core",
|
||||
"2015",
|
||||
"2016",
|
||||
"2017",
|
||||
"2018",
|
||||
"2019",
|
||||
"2020",
|
||||
"2021",
|
||||
"2022",
|
||||
"2023",
|
||||
"2024"
|
||||
]
|
||||
resolver = "2"
|
||||
|
||||
[dependencies]
|
||||
md-5 = "0.10"
|
||||
[workspace.dependencies]
|
||||
md-5 = { version = "0.10" }
|
||||
|
||||
4
core/Cargo.toml
Normal file
4
core/Cargo.toml
Normal file
@ -0,0 +1,4 @@
|
||||
[package]
|
||||
name = "core"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
@ -1,10 +1,13 @@
|
||||
use std::io::{stdout, Write};
|
||||
|
||||
/// AOC Lib
|
||||
/// Generic methods for loading various data inputs from
|
||||
/// the AOC project.
|
||||
|
||||
pub fn data_path(suffix: &str) -> String {
|
||||
let path = std::env::var("CARGO_MANIFEST_DIR").unwrap();
|
||||
format!("{}/data/{}", path, suffix)
|
||||
stdout().flush().unwrap();
|
||||
format!("{}/../data/{}", path, suffix)
|
||||
}
|
||||
|
||||
pub fn read_data(suffix: &str) -> String {
|
||||
Loading…
x
Reference in New Issue
Block a user