7NUHCTMKPF5ZPTU3RRTZ6XCEZC7PZVIEOWIMVH4FG2WTWSXGK6IQC
/*
Potential improvements:
* Hard code lengths for verses 0, 1, 2, and the common strings from verses 3 onwards.
* Calculate extra lengths needed for the common strings from verses 3 onwards using `1 + floor(log10(n))`.
Notes:
* http://www.mattmahoney.net/dc/text.html
* How to create a dictionary: https://groups.google.com/g/comp.compression/c/ZcOTiqck9Tc/m/1KHNFB5ocpoJ.
* cmix pre-processor converts uppercase characters to lowercase characters prepended with escapes codes which decreases symbol vocabulary but increases input length.
*/
/* const ALPHABET: [char; 27] = [
"\n", // 0
" ", // 1
",", // 2
".", // 3
"9", // 4
"G" // 5
"N", // 6
"T", // 7
"a", // 8
"b", // 9
"d" // 10
"e" // 11
"f", // 12
"h", // 13
"i", // 14
"k", // 15
"l", // 16
"m", // 17
"n", // 18
"o", // 19
"p", // 20
"r", // 21
"s", // 22
"t", // 23
"u", // 24
"w", // 25
"y" // 26
]; */
/* "<n length> bottles of beer on the wall, <n length> bottles of beer.
Take one down and pass it around, <(n - 1) length> bottles of beer on the wall.
2 bottles of beer on the wall, 2 bottles of beer.
Take one down and pass it around, 1 bottle of beer on the wall.
1 bottle of beer on the wall, 1 bottle of beer.
Take it down and pass it around, no more bottles of beer on the wall.
No more bottles of beer on the wall, no more bottles of beer.
Go to the store and buy some more, 99 bottles of beer on the wall.
" */
const DICT: [&str; 24] = [
"N", // 0
"o m", // 1
"ore", // 2
" bottle", // 3
"s", // 4
" of beer", // 5
" on", // 6
" th", // 7
"e ", // 8
"wall", // 9
", ", // 10
"n", // 11
".\n", // 12
"Go to", // 13
"st", // 14
" and ", // 15
"buy som", // 16
"m", // 17
"99", // 18
"Take", // 19
" it ", // 20
"down", // 21
"pass", // 22
"around", // 23
];
// const LENGTHS: [usize; 4] = [];
trait CalculateLength {
fn calculate_length(&self) -> usize;
}
struct Indices<'a>(&'a [usize]);
impl CalculateLength for Indices<'_> {
fn calculate_length(&self) -> usize {
self.0.iter().fold(0, |length, index| length + DICT[*index].len())
}
}
struct IndicesWithPrefixes<'a>(&'a [(&'a str, Indices<'a>)]);
impl CalculateLength for IndicesWithPrefixes<'_> {
fn calculate_length(&self) -> usize {
self.0.iter().fold(0, |a, (prefix, indices)| a + prefix.len() + indices.calculate_length())
}
}
impl IndicesWithPrefixes<'_> {
fn generate_verse(&self) -> String {
let mut out = String::with_capacity(self.calculate_length());
self.0.iter().for_each(|(prefix, indices)| {
out.push_str(prefix);
indices.0.iter().for_each(|index| out.push_str(DICT[*index]));
});
out
}
}
pub fn verse(n: u32) -> String {
match n {
0 => {
let indices = Indices(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 1, 2, 3, 4, 5, 12, 13, 7, 8, 14, 2, 15, 16, 8, 17, 2, 10, 18, 3, 4, 5, 6, 7, 8, 9, 12]);
let mut out = String::with_capacity(indices.calculate_length());
indices.0.iter().for_each(|index| out.push_str(DICT[*index]));
out
}
1 => {
let n_string = n.to_string();
IndicesWithPrefixes(&[(&n_string, Indices(&[3, 5, 6, 7, 8, 9, 10])), (&n_string, Indices(&[3, 5, 12, 19, 20, 21, 15, 22, 20, 23, 10, 11, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12]))]).generate_verse()
}
2 => {
let (n_string, m_string) = (n.to_string(), (n - 1).to_string());
IndicesWithPrefixes(&[(&n_string, Indices(&[3, 4, 5, 6, 7, 8, 9, 10])), (&n_string, Indices(&[3, 4, 5, 12, 19, 6, 8, 21, 15, 22, 20, 23, 10])), (&m_string, Indices(&[3, 5, 6, 7, 8, 9, 12]))]).generate_verse()
}
_ => {
let (n_string, m_string) = (n.to_string(), (n - 1).to_string());
IndicesWithPrefixes(&[(&n_string, Indices(&[3, 4, 5, 6, 7, 8, 9, 10])), (&n_string, Indices(&[3, 4, 5, 12, 19, 6, 8, 21, 15, 22, 20, 23, 10])), (&m_string, Indices(&[3, 4, 5, 6, 7, 8, 9, 12]))]).generate_verse()
}
}
}
pub fn sing(start: u32, end: u32) -> String {
"".to_string()
}
/*
Can nncp be used for this?
Ways of encoding data for compression: entropy encoding (Huffman, Arithmetic, Asymmetric Numerical Systems), dictionary encoding (Lempel-Ziv), delta encoding.
How to create a dictionary: https://groups.google.com/g/comp.compression/c/ZcOTiqck9Tc/m/1KHNFB5ocpoJ.
cmix pre-processor converts uppercase characters to lowercase characters prepended with escapes codes which decreases symbol vocabulary but increases input length.
*/
/* const ALPHABET: [char; 27] = [
"\n", // 0
" ", // 1
",", // 2
".", // 3
"9", // 4
"G" // 5
"N", // 6
"T", // 7
"a", // 8
"b", // 9
"d" // 10
"e" // 11
"f", // 12
"h", // 13
"i", // 14
"k", // 15
"l", // 16
"m", // 17
"n", // 18
"o", // 19
"p", // 20
"r", // 21
"s", // 22
"t", // 23
"u", // 24
"w", // 25
"y" // 26
]; */
/* "<n length> bottles of beer on the wall, <n length> bottles of beer.
Take one down and pass it around, <(n - 1) length> bottles of beer on the wall.
2 bottles of beer on the wall, 2 bottles of beer.
Take one down and pass it around, 1 bottle of beer on the wall.
1 bottle of beer on the wall, 1 bottle of beer.
Take it down and pass it around, no more bottles of beer on the wall.
No more bottles of beer on the wall, no more bottles of beer.
Go to the store and buy some more, 99 bottles of beer on the wall.
" */
const DICT: [&str; 24] = [
"N", // 0
"o m", // 1
"ore", // 2
" bottle", // 3
"s", // 4
" of beer", // 5
" on", // 6
" th", // 7
"e ", // 8
"wall", // 9
", ", // 10
"n", // 11
".\n", // 12
"Go to", // 13
"st", // 14
" and ", // 15
"buy som", // 16
"m", // 17
"99", // 18
"Take", // 19
" it ", // 20
"down", // 21
"pass", // 22
"around", // 23
];
// (count, index)
fn calculate_length(strings: &[(usize, usize)]) -> usize {
strings.iter().fold(0, |a, (b, c)| a + b * DICT[*c].len())
}
/*
Common string: " bottle of beer on the wall, bottle of beer.\n, bottle of beer on the wall.\n".
Composed of:
" bottle": 3 of 3
" of beer": 3 of 5
" on": 2 of 6
" th": 2 of 7
"e ": 2 of 8
"wall": 2 of 9
", ": 2 of 10
".\n": 2 of 12
*/
fn common_length() -> usize {
calculate_length(&[(3, 3), (3, 5), (2, 6), (2, 7), (2, 8), (2, 9), (2, 10), (2, 12)])
}
fn allocate_string(strings: &[(usize, usize)], more: usize) -> String {
String::with_capacity(common_length() + calculate_length(strings) + more)
}
fn modify_string(mut string: String, prefices_and_indices: &[(&str, &[usize])]) -> String {
prefices_and_indices.iter().for_each(|(prefix, indices)| {
string.push_str(prefix);
indices.iter().for_each(|index| string.push_str(DICT[*index]));
});
string
}
fn allocate_and_modify_verse(strings: &[(usize, usize)], more: usize, prefices_and_indices: &[(&str, &[usize])]) -> String {
let mut out = String::with_capacity(common_length() + calculate_length(strings) + more);
prefices_and_indices.iter().for_each(|(prefix, indices)| {
out.push_str(prefix);
indices.iter().for_each(|index| out.push_str(DICT[*index]));
});
out
}
// TODO: Refactor common code blocks.
pub fn verse(n: u32) -> String {
match n {
// Easy mode: 0 => [0usize, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 1, 2, 3, 4, 5, 12, 13, 7, 8, 14, 2, 15, 16, 8, 17, 2, 10, 18, 3, 4, 5, 6, 7, 8, 9, 12].iter().map(|a| DICT[*a]).collect::<String>(),
0 => {
/*
Extras are:
"N": 1 of 0
"o m": 2 of 1
"ore": 4 of 2
"s": 3 of 4
"n": 1 of 11
"Go to": 1 of 13
" th": 1 of 7
"e ": 2 of 8
"st": 1 of 14
" and ": 1 of 15
"buy som": 1 of 16
"m": 1 of 17
"99": 1 of 18
*/
// assert_eq!("No more bottles of beer on the wall, no more bottles of beer.\nGo to the store and buy some more, 99 bottles of beer on the wall.\n".len(), common_length + calculate_length(&[(1, 0), (2, 1), (4, 2), (3, 4), (1, 11), (1, 13), (1, 7), (2, 8), (1, 14), (1, 15), (1, 16), (1, 17), (1, 18)]));
allocate_and_modify_verse(&[(1, 0), (2, 1), (4, 2), (3, 4), (1, 11), (1, 13), (1, 7), (2, 8), (1, 14), (1, 15), (1, 16), (1, 17), (1, 18)], 0, &[(&"", &[0usize, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 1, 2, 3, 4, 5, 12, 13, 7, 8, 14, 2, 15, 16, 8, 17, 2, 10, 18, 3, 4, 5, 6, 7, 8, 9, 12])])
}
1 => {
/*
Extras are:
n: 2
"Take": 1 of 19
" it ": 2 of 20
"down": 1 of 21
" and ": 1 of 15
"pass": 1 of 22
"around": 1 of 23
"n": 1 of 11
"o m": 1 of 1
"ore": 1 of 2
"s": 1 of 4
*/
let n_string = n.to_string();
// assert_eq!("1 bottle of beer on the wall, 1 bottle of beer.\nTake it down and pass it around, no more bottles of beer on the wall.\n".len(), common_length + 2 + calculate_length(&[(1, 19), (2, 20), (1, 21), (1, 15), (1, 22), (1, 23), (1, 11), (1, 1), (1, 2), (1, 4)]));
allocate_and_modify_verse(&[(1, 19), (2, 20), (1, 21), (1, 15), (1, 22), (1, 23), (1, 11), (1, 1), (1, 2), (1, 4)], 2, &[(&n_string, &[3usize, 5, 6, 7, 8, 9, 10]), (&n_string, &[3usize, 5, 12, 19, 20, 21, 15, 22, 20, 23, 10, 11, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12])])
}
2 => {
/*
Extras are:
n: 2
"s": 2 of 4
"Take": 1 of 19
" on": 1 of 6
"e ": 1 of 8
"down": 1 of 21
" and ": 1 of 15
"pass": 1 of 22
" it": 1 of 20
"around": 1 of 23
n - 1: 1
*/
let (n_string, m_string) = (n.to_string(), (n - 1).to_string());
// assert_eq!("2 bottles of beer on the wall, 2 bottles of beer.\nTake one down and pass it around, 1 bottle of beer on the wall.\n".len(), common_length + 2 * n_string.len() + calculate_length(&[(2, 4), (1, 19), (1, 6), (1, 8), (1, 21), (1, 15), (1, 22), (1, 20), (1, 23)]) + m_string.len());
allocate_and_modify_verse(&[(2, 4), (1, 19), (1, 6), (1, 8), (1, 21), (1, 15), (1, 22), (1, 20), (1, 23)], 2 * n_string.len() + m_string.len(), &[(&n_string, &[3usize, 4, 5, 6, 7, 8, 9, 10]), (&n_string, &[3usize, 4, 5, 12, 19, 6, 8, 21, 15, 22, 20, 23, 10]), (&m_string, &[3, 5, 6, 7, 8, 9, 12])])
}
_ => {
/*
Extras are:
n: 2
"s": 3 of 4
"Take": 1 of 19
" on": 1 of 6
"e ": 1 of 8
"down": 1 of 21
" and ": 1 of 15
"pass": 1 of 22
" it": 1 of 20
"around": 1 of 23
n - 1: 1
*/
let (n_string, m_string) = (n.to_string(), (n - 1).to_string());
// assert_eq!("3 bottles of beer on the wall, 3 bottles of beer.\nTake one down and pass it around, 2 bottles of beer on the wall.\n".len(), common_length + 2 * n_string.len() + calculate_length(&[(3, 4), (1, 19), (1, 6), (1, 8), (1, 21), (1, 15), (1, 22), (1, 20), (1, 23)]) + m_string.len());
allocate_and_modify_verse(&[(3, 4), (1, 19), (1, 6), (1, 8), (1, 21), (1, 15), (1, 22), (1, 20), (1, 23)], 2 * n_string.len() + m_string.len(), &[(&n_string, &[3usize, 4, 5, 6, 7, 8, 9, 10]), (&n_string, &[3usize, 4, 5, 12, 19, 6, 8, 21, 15, 22, 20, 23, 10]), (&m_string, &[3, 4, 5, 6, 7, 8, 9, 12])])
}
}
}
pub fn sing(start: u32, end: u32) -> String {
"".to_string()
// (start..=end).for_each(verse)
}
" on", // 6
" th", // 7
"e ", // 8
"wall", // 9
", ", // 10
"n", // 11
".\n", // 12
"Go to", // 13
"st", // 14
" and ", // 15
"buy som", // 16
"m", // 17
"99", // 18
"Take", // 19
" it ", // 20
"down", // 21
"pass", // 22
"around", // 23
" on", // 6
" th", // 7
"e ", // 8
"wall", // 9
", ", // 10
"n", // 11
".\n", // 12
"Go to", // 13
"st", // 14
" and ", // 15
"buy som", // 16
"m", // 17
"99", // 18
"Take", // 19
" it ", // 20
"down", // 21
"pass", // 22
"around", // 23
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 1, 2, 3, 4, 5, 12, 13, 7, 8, 14, 2, 15, 16, 8, 17, 2, 10, 18, 3, 4, 5, 6, 7, 8, 9, 12].iter().for_each(|index| out.push_str(DICT[*index]));
[
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 1, 2, 3, 4, 5, 12, 13, 7, 8, 14, 2, 15, 16,
8, 17, 2, 10, 18, 3, 4, 5, 6, 7, 8, 9, 12,
]
.iter()
.for_each(|index| out.push_str(DICT[*index]));
IndicesWithPrefixes(&[(&n_string, &[3, 5, 6, 7, 8, 9, 10]), (&n_string, &[3, 5, 12, 19, 20, 21, 15, 22, 20, 23, 10, 11, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12])]).generate_verse(n)
IndicesWithPrefixes(&[
(&n_string, &[3, 5, 6, 7, 8, 9, 10]),
(
&n_string,
&[
3, 5, 12, 19, 20, 21, 15, 22, 20, 23, 10, 11, 1, 2, 3, 4, 5, 6, 7, 8, 9, 12,
],
),
])
.generate_verse(n)
IndicesWithPrefixes(&[(&n_string, &[3, 4, 5, 6, 7, 8, 9, 10]), (&n_string, &[3, 4, 5, 12, 19, 6, 8, 21, 15, 22, 20, 23, 10]), (&m_string, &[3, 5, 6, 7, 8, 9, 12])]).generate_verse(n)
IndicesWithPrefixes(&[
(&n_string, &[3, 4, 5, 6, 7, 8, 9, 10]),
(&n_string, &[3, 4, 5, 12, 19, 6, 8, 21, 15, 22, 20, 23, 10]),
(&m_string, &[3, 5, 6, 7, 8, 9, 12]),
])
.generate_verse(n)
IndicesWithPrefixes(&[(&n_string, &[3, 4, 5, 6, 7, 8, 9, 10]), (&n_string, &[3, 4, 5, 12, 19, 6, 8, 21, 15, 22, 20, 23, 10]), (&m_string, &[3, 4, 5, 6, 7, 8, 9, 12])]).generate_verse(n)
IndicesWithPrefixes(&[
(&n_string, &[3, 4, 5, 6, 7, 8, 9, 10]),
(&n_string, &[3, 4, 5, 12, 19, 6, 8, 21, 15, 22, 20, 23, 10]),
(&m_string, &[3, 4, 5, 6, 7, 8, 9, 12]),
])
.generate_verse(n)
let (a, b) = ((10_f64.powi(start_digits.try_into().unwrap()) - 1.0) as usize, 10_f64.powi((end_digits - 1).try_into().unwrap()) as usize);
(integer_length(a + 1)..=integer_length(b - 1)).fold(0, |acc, elem| acc + 9 * elem * (10_f64.powi((elem - 1).try_into().unwrap()) as usize)) + ((a - start + 1) * start_digits) + ((end - b + 1) * end_digits)
let (a, b) = (
(10_f64.powi(start_digits.try_into().unwrap()) - 1.0) as usize,
10_f64.powi((end_digits - 1).try_into().unwrap()) as usize,
);
(integer_length(a + 1)..=integer_length(b - 1)).fold(0, |acc, elem| {
acc + 9 * elem * (10_f64.powi((elem - 1).try_into().unwrap()) as usize)
}) + ((a - start + 1) * start_digits)
+ ((end - b + 1) * end_digits)
(_, _) => (s as usize - 2) * LENGTHS[3] + integer_range_length(3, s as usize) + match e {
0 => LENGTHS[0] + LENGTHS[1] + LENGTHS[2],
1 => LENGTHS[1] + LENGTHS[2],
2 => LENGTHS[2],
_ => 0,
},
} + ((s - e) as usize)
(_, _) => {
(s as usize - 2) * LENGTHS[3]
+ integer_range_length(3, s as usize)
+ match e {
0 => LENGTHS[0] + LENGTHS[1] + LENGTHS[2],
1 => LENGTHS[1] + LENGTHS[2],
2 => LENGTHS[2],
_ => 0,
}
}
} + ((s - e) as usize),