summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Sayers <alex@asayers.com>2023-04-06 22:31:17 +0900
committerGitHub <noreply@github.com>2023-04-06 15:31:17 +0200
commit5cb226026e104c0ddb855c3ad6fee5212a796006 (patch)
treed10b663d7486c7a8b954b6c62f15e6350b006008
parent0f8219b392e96d3cf7d784ee5d474274169d9918 (diff)
Add Iroha-style numbering (+ bugfix) (#555)
-rw-r--r--library/src/meta/numbering.rs109
-rw-r--r--tests/ref/meta/numbering.pngbin39055 -> 29684 bytes
-rw-r--r--tests/typ/meta/numbering.typ42
3 files changed, 124 insertions, 27 deletions
diff --git a/library/src/meta/numbering.rs b/library/src/meta/numbering.rs
index f155abf8..aed2a9e3 100644
--- a/library/src/meta/numbering.rs
+++ b/library/src/meta/numbering.rs
@@ -36,8 +36,9 @@ use crate::text::Case;
pub fn numbering(
/// Defines how the numbering works.
///
- /// **Counting symbols** are `1`, `a`, `A`, `i`, `I` and `*`. They are
- /// replaced by the number in the sequence, in the given case.
+ /// **Counting symbols** are `1`, `a`, `A`, `i`, `I`, `い`, `イ`,
+ /// `א`, and `*`. They are replaced by the number in the sequence,
+ /// in the given case.
///
/// The `*` character means that symbols should be used to count, in the
/// order of `*`, `†`, `‡`, `§`, `¶`, and `‖`. If there are more than six
@@ -129,8 +130,8 @@ cast_to_value! {
/// How to turn a number into text.
///
-/// A pattern consists of a prefix, followed by one of `1`, `a`, `A`, `i`, `I`
-/// or `*`, and then a suffix.
+/// A pattern consists of a prefix, followed by one of `1`, `a`, `A`, `i`,
+/// `I`, `い`, `イ`, `א`, or `*`, and then a suffix.
///
/// Examples of valid patterns:
/// - `1)`
@@ -256,6 +257,8 @@ enum NumberingKind {
Roman,
Symbol,
Hebrew,
+ HiraganaIroha,
+ KatakanaIroha,
}
impl NumberingKind {
@@ -267,6 +270,8 @@ impl NumberingKind {
'i' => NumberingKind::Roman,
'*' => NumberingKind::Symbol,
'א' => NumberingKind::Hebrew,
+ 'い' => NumberingKind::HiraganaIroha,
+ 'イ' => NumberingKind::KatakanaIroha,
_ => return None,
})
}
@@ -279,6 +284,8 @@ impl NumberingKind {
Self::Roman => 'i',
Self::Symbol => '*',
Self::Hebrew => 'א',
+ Self::HiraganaIroha => 'い',
+ Self::KatakanaIroha => 'イ',
}
}
@@ -288,29 +295,37 @@ impl NumberingKind {
Self::Arabic => {
eco_format!("{n}")
}
- Self::Letter => {
- if n == 0 {
- return '-'.into();
- }
-
- n -= 1;
-
- let mut letters = vec![];
- loop {
- let c = b'a' + (n % 26) as u8;
- letters.push(match case {
- Case::Lower => c,
- Case::Upper => c.to_ascii_uppercase(),
- });
- n /= 26;
- if n == 0 {
- break;
- }
- }
-
- letters.reverse();
- String::from_utf8(letters).unwrap().into()
- }
+ Self::Letter => zeroless::<26>(
+ |x| match case {
+ Case::Lower => char::from(b'a' + x as u8),
+ Case::Upper => char::from(b'A' + x as u8),
+ },
+ n,
+ ),
+ Self::HiraganaIroha => zeroless::<47>(
+ |x| {
+ [
+ 'い', 'ろ', 'は', 'に', 'ほ', 'へ', 'と', 'ち', 'り', 'ぬ', 'る',
+ 'を', 'わ', 'か', 'よ', 'た', 'れ', 'そ', 'つ', 'ね', 'な', 'ら',
+ 'む', 'う', 'ゐ', 'の', 'お', 'く', 'や', 'ま', 'け', 'ふ', 'こ',
+ 'え', 'て', 'あ', 'さ', 'き', 'ゆ', 'め', 'み', 'し', 'ゑ', 'ひ',
+ 'も', 'せ', 'す',
+ ][x]
+ },
+ n,
+ ),
+ Self::KatakanaIroha => zeroless::<47>(
+ |x| {
+ [
+ 'イ', 'ロ', 'ハ', 'ニ', 'ホ', 'ヘ', 'ト', 'チ', 'リ', 'ヌ', 'ル',
+ 'ヲ', 'ワ', 'カ', 'ヨ', 'タ', 'レ', 'ソ', 'ツ', 'ネ', 'ナ', 'ラ',
+ 'ム', 'ウ', 'ヰ', 'ノ', 'オ', 'ク', 'ヤ', 'マ', 'ケ', 'フ', 'コ',
+ 'エ', 'テ', 'ア', 'サ', 'キ', 'ユ', 'メ', 'ミ', 'シ', 'ヱ', 'ヒ',
+ 'モ', 'セ', 'ス',
+ ][x]
+ },
+ n,
+ ),
Self::Roman => {
if n == 0 {
return 'N'.into();
@@ -420,3 +435,43 @@ impl NumberingKind {
}
}
}
+
+/// Stringify a number using a base-N counting system with no zero digit.
+///
+/// This is best explained by example. Suppose our digits are 'A', 'B', and 'C'.
+/// we would get the following:
+///
+/// ```text
+/// 1 => "A"
+/// 2 => "B"
+/// 3 => "C"
+/// 4 => "AA"
+/// 5 => "AB"
+/// 6 => "AC"
+/// 7 => "BA"
+/// 8 => "BB"
+/// 9 => "BC"
+/// 10 => "CA"
+/// 11 => "CB"
+/// 12 => "CC"
+/// 13 => "AAA"
+/// etc.
+/// ```
+///
+/// You might be familiar with this scheme from the way spreadsheet software
+/// tends to label its columns.
+fn zeroless<const N_DIGITS: usize>(
+ mk_digit: impl Fn(usize) -> char,
+ mut n: usize,
+) -> EcoString {
+ if n == 0 {
+ return '-'.into();
+ }
+ let mut cs = vec![];
+ while n > 0 {
+ n -= 1;
+ cs.push(mk_digit(n % N_DIGITS));
+ n /= N_DIGITS;
+ }
+ cs.into_iter().rev().collect()
+}
diff --git a/tests/ref/meta/numbering.png b/tests/ref/meta/numbering.png
index 998efe57..2c5db2d5 100644
--- a/tests/ref/meta/numbering.png
+++ b/tests/ref/meta/numbering.png
Binary files differ
diff --git a/tests/typ/meta/numbering.typ b/tests/typ/meta/numbering.typ
index cf8104ed..100fa480 100644
--- a/tests/typ/meta/numbering.typ
+++ b/tests/typ/meta/numbering.typ
@@ -10,6 +10,25 @@
}
---
+#for i in range(0, 4) {
+ numbering("A", i)
+ [ for #i]
+ linebreak()
+}
+#par[...]
+#for i in range(26, 30) {
+ numbering("A", i)
+ [ for #i]
+ linebreak()
+}
+#par[...]
+#for i in range(702, 706) {
+ numbering("A", i)
+ [ for #i]
+ linebreak()
+}
+
+---
// Error: 17-19 number must be at least zero
#numbering("1", -1)
@@ -21,3 +40,26 @@
[ עבור #i]
parbreak()
}
+
+---
+#for i in range(0, 4) {
+ numbering("イ", i)
+ [ (or ]
+ numbering("い", i)
+ [) for #i]
+ linebreak()
+}
+#par[...]
+#for i in range(47, 51) {
+ numbering("イ", i)
+ [ (or ]
+ numbering("い", i)
+ [) for #i]
+ linebreak()
+}
+#par[...]
+#for i in range(2256, 2260) {
+ numbering("イ", i)
+ [ for #i]
+ linebreak()
+}