I wrote the following Rust code to solve this task on /r/DailyProgrammer.
Given an n-tuple of numbers as input, the Ducci Sequence is formed by taking the absolute difference of the difference between neighboring elements. (The last result is the difference between the first and last elements.)
Example input
[0; 653; 1854; 4063]
Example output
Emit the number of steps taken to get to either an all 0 tuple or when it enters a stable repeating pattern.
[0; 653; 1854; 4063] [653; 1201; 2209; 4063] [548; 1008; 1854; 3410] [460; 846; 1556; 2862] [386; 710; 1306; 2402] [324; 596; 1096; 2016] [272; 500; 920; 1692] [228; 420; 772; 1420] [192; 352; 648; 1192] [160; 296; 544; 1000] [136; 248; 456; 840] [112; 208; 384; 704] [96; 176; 320; 592] [80; 144; 272; 496] [64; 128; 224; 416] [64; 96; 192; 352] [32; 96; 160; 288] [64; 64; 128; 256] [0; 64; 128; 192] [64; 64; 64; 192] [0; 0; 128; 128] [0; 128; 0; 128] [128; 128; 128; 128] [0; 0; 0; 0] 24 steps
I'm aware all the extra functions and error handling are a bit exaggerated for a task this easy, but I'm learning rust at the moment so I tried to get things right.
extern crate regex;
use std::io::Write;
use std::fmt::Display;
use std::fmt;
use regex::Regex;
use std::io;
use std::error;
fn main() {
match read_input() {
Ok(tuple) => {
run(tuple);
}
Err(e) => {
eprintln!("An error occured: {}", e);
}
}
}
/// Tries to read input from standard input
fn read_input() -> Result<Tuple, Error> {
print!("input: ");
io::stdout().flush().unwrap();
let mut input = String::new();
io::stdin().read_line(&mut input)?;
Tuple::new(input)
}
/// runs Ducci sequence calculation and prints every step
fn run(tuple: Tuple) {
let mut i = tuple;
let mut history: Vec<Tuple> = Vec::new();
let mut steps = 1;
while !&i.zeros() && !history.contains(&i) {
let next = i.next();
history.push(i);
i = next;
println!("{}", i);
steps += 1;
}
println!("{} steps", steps);
}
struct Tuple {
data: Vec<i32>,
}
impl Tuple {
fn new(line: String) -> Result<Tuple, Error> {
// Regex for allowed inputs: (a, b, c, ..., d)
let re = Regex::new(r"\(((\d)+, )*(\d)+\)").unwrap();
if!re.is_match(line.as_str()) {
Err(Error::ParsingError)
}
else {
// seperate single numbers, parse to integer and push into tuple instance
let sep = Regex::new(r"(, |\(|\))").unwrap();
let mut data: Vec<i32> = Vec::new();
for numbers in sep.split(line.as_str()) {
match numbers.parse::<i32>() {
Ok(number) => {
data.push(number);
},
// ignore newline and empty captures
Err(_) => {},
}
}
Ok(Tuple {
data: data,
})
}
}
/// Calculates and returns next tuple in ducci sequence
fn next(&self) -> Tuple {
let mut data: Vec<i32> = Vec::new();
// calculate first n - 1 new values
for i in 0..self.dimension() - 1 {
data.push((self.data[i] - self.data[i + 1]).abs());
}
// calculate last value
data.push((self.data[self.dimension() - 1] - self.data[0]).abs());
Tuple {
data: data,
}
}
/// Returns tuples dimension
fn dimension(&self) -> usize {
self.data.len()
}
/// Determines whether tuple only contains zeros
fn zeros(&self) -> bool {
self.data.iter().fold(true, |acc, x| {acc && *x == 0})
}
}
impl PartialEq for Tuple {
fn eq(&self, other: &Tuple) -> bool {
if self.dimension() != other.dimension() {
false
}
else {
let mut e = true;
for i in 0..self.dimension() {
e = e && self.data[i] == other.data[i];
}
e
}
}
}
impl Display for Tuple {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut s = String::new();
s.push_str("[");
for i in 0..self.dimension() - 1 {
s.push_str(self.data[i].to_string().as_str());
s.push_str("; ");
}
s.push_str(self.data[self.dimension() - 1].to_string().as_str());
s.push_str("]");
write!(f, "{}", s)
}
}
#[derive(Debug)]
enum Error {
ReadingError,
ParsingError,
}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match self {
Error::ReadingError => {
f.write_str("Error while reading input line from standard input.")
}
Error::ParsingError => {
f.write_str("Input line does not meet format requirements: (a, b, c, ..., d)")
}
}
}
}
impl error::Error for Error {}
impl std::convert::From<std::io::Error> for Error {
fn from(_e: std::io::Error) -> Self {
Error::ReadingError
}
}
What are youre thoughts? What can I do better or in a more elegant way?