(back to exercise)
pub fn luhn(cc_number: &str) -> bool {
let mut digits_seen = 0;
let mut sum = 0;
for (i, ch) in cc_number.chars().rev().filter(|&ch| ch != ' ').enumerate() {
match ch.to_digit(10) {
Some(d) => {
sum += if i % 2 == 1 {
let dd = d * 2;
dd / 10 + dd % 10
} else {
d
};
digits_seen += 1;
}
None => return false,
}
}
if digits_seen < 2 {
return false;
}
sum % 10 == 0
}
fn main() {
let cc_number = "1234 5678 1234 5670";
println!(
"Is {cc_number} a valid credit card number? {}",
if luhn(cc_number) { "yes" } else { "no" }
);
}
#[test]
fn test_non_digit_cc_number() {
assert!(!luhn("foo"));
assert!(!luhn("foo 0 0"));
}
#[test]
fn test_empty_cc_number() {
assert!(!luhn(""));
assert!(!luhn(" "));
assert!(!luhn(" "));
assert!(!luhn(" "));
}
#[test]
fn test_single_digit_cc_number() {
assert!(!luhn("0"));
}
#[test]
fn test_two_digit_cc_number() {
assert!(luhn(" 0 0 "));
}
#[test]
fn test_valid_cc_number() {
assert!(luhn("4263 9826 4026 9299"));
assert!(luhn("4539 3195 0343 6467"));
assert!(luhn("7992 7398 713"));
}
#[test]
fn test_invalid_cc_number() {
assert!(!luhn("4223 9826 4026 9299"));
assert!(!luhn("4539 3195 0343 6476"));
assert!(!luhn("8273 1232 7352 0569"));
}
/// An operation to perform on two subexpressions.
#[derive(Debug)]
enum Operation {
Add,
Sub,
Mul,
Div,
}
/// An expression, in tree form.
#[derive(Debug)]
enum Expression {
/// An operation on two subexpressions.
Op {
op: Operation,
left: Box<Expression>,
right: Box<Expression>,
},
/// A literal value
Value(i64),
}
/// The result of evaluating an expression.
#[derive(Debug, PartialEq, Eq)]
enum Res {
/// Evaluation was successful, with the given result.
Ok(i64),
/// Evaluation failed, with the given error message.
Err(String),
}
// Allow `Ok` and `Err` as shorthands for `Res::Ok` and `Res::Err`.
use Res::{Err, Ok};
fn eval(e: Expression) -> Res {
match e {
Expression::Op { op, left, right } => {
let left = match eval(*left) {
Ok(v) => v,
Err(msg) => return Err(msg),
};
let right = match eval(*right) {
Ok(v) => v,
Err(msg) => return Err(msg),
};
Ok(match op {
Operation::Add => left + right,
Operation::Sub => left - right,
Operation::Mul => left * right,
Operation::Div => {
if right == 0 {
return Err(String::from("division by zero"));
} else {
left / right
}
}
})
}
Expression::Value(v) => Ok(v),
}
}
#[test]
fn test_value() {
assert_eq!(eval(Expression::Value(19)), Ok(19));
}
#[test]
fn test_sum() {
assert_eq!(
eval(Expression::Op {
op: Operation::Add,
left: Box::new(Expression::Value(10)),
right: Box::new(Expression::Value(20)),
}),
Ok(30)
);
}
#[test]
fn test_recursion() {
let term1 = Expression::Op {
op: Operation::Mul,
left: Box::new(Expression::Value(10)),
right: Box::new(Expression::Value(9)),
};
let term2 = Expression::Op {
op: Operation::Mul,
left: Box::new(Expression::Op {
op: Operation::Sub,
left: Box::new(Expression::Value(3)),
right: Box::new(Expression::Value(4)),
}),
right: Box::new(Expression::Value(5)),
};
assert_eq!(
eval(Expression::Op {
op: Operation::Add,
left: Box::new(term1),
right: Box::new(term2),
}),
Ok(85)
);
}
#[test]
fn test_error() {
assert_eq!(
eval(Expression::Op {
op: Operation::Div,
left: Box::new(Expression::Value(99)),
right: Box::new(Expression::Value(0)),
}),
Err(String::from("division by zero"))
);
}
fn main() {
let expr = Expression::Op {
op: Operation::Sub,
left: Box::new(Expression::Value(20)),
right: Box::new(Expression::Value(10)),
};
println!("expr: {:?}", expr);
println!("result: {:?}", eval(expr));
}