Skip to content

Commit

Permalink
v1.0.0 functionally complete
Browse files Browse the repository at this point in the history
  • Loading branch information
jldec committed Feb 22, 2023
1 parent bb9b026 commit 6ab4a4b
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 16 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
__pycache__
.venv
.vscode/
__pycache__/
dist/
*.egg-info/
.pytest_cache/
14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

[![CI](https://github.com/jldec/shortscale-py/workflows/CI/badge.svg)](https://github.com/jldec/shortscale-py/actions)

Python module to convert numbers into English words.
Python module to convert integers into English words.

This is the Python port of the shortscale function, originally written in [JavaScript](https://github.com/jldec/shortscale) and [Rust](https://github.com/jldec/shortscale-rs), documented [here](https://jldec.me/forays-from-node-to-rust). There is a also a [Go](https://github.com/jldec/shortscale-go) version.

The [short scale](https://en.wikipedia.org/wiki/Long_and_short_scales#Comparison), has different words for each power of 1000.

This implementation expresses numbers from zero to thousands, millions, billions, trillions, and quadrillions, up to 999_999_999_999_999_999.
This implementation expresses positive and negative numbers from zero to thousands, millions, billions, trillions, quadrillions etc, up to 10**33 - 1.

### Function
```python
Expand All @@ -26,10 +26,12 @@ print(shortscale.shortscale(420_000_999_015))
After installing this module, the function can also be called from the commnd line e.g.

```sh
shortscale 420_000_999_015
$ shortscale 420_000_999_015
420,000,999,015 => four hundred and twenty billion nine hundred and ninety nine thousand and fifteen

$ shortscale 0xffffffff
4,294,967,295 => four billion two hundred and ninety four million nine hundred and sixty seven thousand two hundred and ninety five
```

### Benchmarks

```
```
(TODO)
118 changes: 109 additions & 9 deletions shortscale.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,120 @@
"""English conversion from number to string"""
import sys

__version__ = "0.1.0"
__version__ = '1.0.0'


def shortscale(num: int) -> str:
return '{} ({} bits)'.format(num, num.bit_length())
words = []

if num < 0:
words.append('minus')
num = -num

def main():
if len(sys.argv) < 2:
print ('Usage: shortscale num')
sys.exit(1)
if num <= 20:
words.append(numwords[num]) # 0 - 20
elif num >= 1000 ** 11:
words.append('(big number)')
else:
for (n, exponent) in powers_of_1000(num):
scale_words(n, exponent, words)

return ' '.join(words)


def powers_of_1000(n: int) -> list[(int, int)]:
p_list = []
exponent = 0
while n > 0:
p_list.insert(0, (n % 1000, exponent))
n = n // 1000
exponent += 1

return p_list


def scale_words(n: int, exponent: int, words: list[str]):
if n == 0:
return

if hundreds := n // 100:
words.append(numwords[hundreds])
words.append(numwords[100])

if tens_and_units := n % 100:

print(shortscale(int(sys.argv[1],0)))
sys.exit(0)
if hundreds or (exponent == 0 and len(words)):
words.append('and')

if tens_and_units < 20:
words.append(numwords[tens_and_units])

else:
if tens := tens_and_units // 10:
words.append(numwords[tens * 10])

if units := tens_and_units % 10:
words.append(numwords[units])

if exponent:
words.append(numwords[1000 ** exponent])


numwords = {
0: 'zero',
1: 'one',
2: 'two',
3: 'three',
4: 'four',
5: 'five',
6: 'six',
7: 'seven',
8: 'eight',
9: 'nine',
10: 'ten',
11: 'eleven',
12: 'twelve',
13: 'thirteen',
14: 'fourteen',
15: 'fifteen',
16: 'sixteen',
17: 'seventeen',
18: 'eighteen',
19: 'nineteen',
20: 'twenty',
30: 'thirty',
40: 'forty',
50: 'fifty',
60: 'sixty',
70: 'seventy',
80: 'eighty',
90: 'ninety',
100: 'hundred',
1000: 'thousand',
1000_000: 'million', # 1000 ** 2
1000_000_000: 'billion', # 1000 ** 3
1000_000_000_000: 'trillion', # 1000 ** 4
1000_000_000_000_000: 'quadrillion', # 1000 ** 5
1000_000_000_000_000_000: 'quintillion', # 1000 ** 6
1000_000_000_000_000_000_000: 'sextillion', # 1000 ** 7
1000_000_000_000_000_000_000_000: 'septillion', # 1000 ** 8
1000_000_000_000_000_000_000_000_000: 'octillion', # 1000 ** 9
1000_000_000_000_000_000_000_000_000_000: 'nonillion' # 1000 ** 10
}


def main():
if len(sys.argv) < 2:
print('usage: shortscale num')
sys.exit(1)
try:
num = int(sys.argv[1], 0)
print(f'{num:,} => {shortscale(num)}')
sys.exit(0)
except Exception as err:
print(f'Oops! {err}')
sys.exit(2)


if __name__ == '__main__':
main()
main()
63 changes: 63 additions & 0 deletions tests/test_shortscale.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import pytest
import shortscale


def test_shortscale():
for (num, s) in TESTS:
assert shortscale.shortscale(num) == s


TESTS = [
(0, "zero"),
(1, "one"),
(-1, "minus one"),
(10, "ten"),
(11, "eleven"),
(18, "eighteen"),
(20, "twenty"),
(22, "twenty two"),
(30, "thirty"),
(33, "thirty three"),
(40, "forty"),
(111, "one hundred and eleven"),
(120, "one hundred and twenty"),
(121, "one hundred and twenty one"),
(300, "three hundred"),
(999, "nine hundred and ninety nine"),
(1_000, "one thousand"),
(2_000, "two thousand"),
(2_004, "two thousand and four"),
(2_011, "two thousand and eleven"),
(2_020, "two thousand and twenty"),
(2_050, "two thousand and fifty"),
(2_300, "two thousand three hundred"),
(2_301, "two thousand three hundred and one"),
(30_020, "thirty thousand and twenty"),
(430_020, "four hundred and thirty thousand and twenty"),
(430_920, "four hundred and thirty thousand nine hundred and twenty"),
(999_001, "nine hundred and ninety nine thousand and one"),
(999_120, "nine hundred and ninety nine thousand one hundred and twenty"),
(1_000_000, "one million"),
(1_001_000, "one million one thousand"),
(1_002_000, "one million two thousand"),
(1_002_004, "one million two thousand and four"),
(1_002_011, "one million two thousand and eleven"),
(1_002_020, "one million two thousand and twenty"),
(1_002_050, "one million two thousand and fifty"),
(1_002_300, "one million two thousand three hundred"),
(1_002_301, "one million two thousand three hundred and one"),
(1_030_020, "one million thirty thousand and twenty"),
(1_430_020, "one million four hundred and thirty thousand and twenty"),
(1_430_920, "one million four hundred and thirty thousand nine hundred and twenty"),
(1_999_001, "one million nine hundred and ninety nine thousand and one"),
(100_999_120, "one hundred million nine hundred and ninety nine thousand one hundred and twenty"),
(999_999_120, "nine hundred and ninety nine million nine hundred and ninety nine thousand one hundred and twenty"),
(420_000_999_015, "four hundred and twenty billion nine hundred and ninety nine thousand and fifteen"),
(9_007_199_254_740_999, "nine quadrillion seven trillion one hundred and ninety nine billion two hundred and fifty four million seven hundred and forty thousand nine hundred and ninety nine"),
(999_999_999_999_999_999, "nine hundred and ninety nine quadrillion nine hundred and ninety nine trillion nine hundred and ninety nine billion nine hundred and ninety nine million nine hundred and ninety nine thousand nine hundred and ninety nine"),
(777_777_777_777_777_777, "seven hundred and seventy seven quadrillion seven hundred and seventy seven trillion seven hundred and seventy seven billion seven hundred and seventy seven million seven hundred and seventy seven thousand seven hundred and seventy seven"),
(1_999_999_999_999_999_999, "one quintillion nine hundred and ninety nine quadrillion nine hundred and ninety nine trillion nine hundred and ninety nine billion nine hundred and ninety nine million nine hundred and ninety nine thousand nine hundred and ninety nine"),
(999_999_999_999_999_999_999_999_999, "nine hundred and ninety nine septillion nine hundred and ninety nine sextillion nine hundred and ninety nine quintillion nine hundred and ninety nine quadrillion nine hundred and ninety nine trillion nine hundred and ninety nine billion nine hundred and ninety nine million nine hundred and ninety nine thousand nine hundred and ninety nine"),
(999_999_999_999_999_999_999_999_999_999_999, "nine hundred and ninety nine nonillion nine hundred and ninety nine octillion nine hundred and ninety nine septillion nine hundred and ninety nine sextillion nine hundred and ninety nine quintillion nine hundred and ninety nine quadrillion nine hundred and ninety nine trillion nine hundred and ninety nine billion nine hundred and ninety nine million nine hundred and ninety nine thousand nine hundred and ninety nine"),
(999_999_999_999_999_999_999_999_999_999_999+1, "(big number)"),
]

0 comments on commit 6ab4a4b

Please sign in to comment.