cfad47cfa3/t3compiler/tads3/test/data/adv3_num.t

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
1
/* Copyright 2000, 2002 Michael J. Roberts */
2
/*
3
 *   TADS 3 library - number handling 
4
 */
5
6
#include "adv3.h"
7
8
9
/* ------------------------------------------------------------------------ */
10
/*
11
 *   Spell out an integer number in words.  Returns a string with the
12
 *   spelled-out number.  
13
 */
14
spellInt(val)
15
{
16
    return spellIntExt(val, 0);
17
}
18
19
/*
20
 *   Spell out an integer number in words, but only if it's below the
21
 *   given threshhold.  It's often awkward in prose to spell out large
22
 *   numbers, but exactly what constitutes a large number depends on
23
 *   context, so this routine lets the caller specify the threshhold.
24
 *   
25
 *   If the absolute value of val is less than (not equal to) the
26
 *   threshhold value, we'll return a string with the number spelled out.
27
 *   If the absolute value is greater than or equal to the threshhold
28
 *   value, we'll return a string representing the number in decimal
29
 *   digits.  
30
 */
31
spellIntBelow(val, threshhold)
32
{
33
    return spellIntBelowExt(val, threshhold, 0, 0);
34
}
35
36
/*
37
 *   Spell out an integer number in words if it's below a threshhold,
38
 *   using the SPELLINT_xxx flags given in spellFlags to control the
39
 *   spelled-out format, and using the INTDEC_xxx flags in digitFlags to
40
 *   control the digit format.  
41
 */
42
spellIntBelowExt(val, threshhold, spellFlags, digitFlags)
43
{
44
    local absval;
45
46
    /* compute the absolute value */
47
    absval = (val < 0 ? -val : val);
48
49
    /* check the value to see whether to spell it or write it as digits */
50
    if (absval < threshhold)
51
    {
52
        /* it's below the threshhold - spell it out in words */
53
        return spellIntExt(val, spellFlags);
54
    }
55
    else
56
    {
57
        /* it's not below the threshhold - write it as digits */
58
        return intToDecimal(val, digitFlags);
59
    }
60
}
61
62
/*
63
 *   Format a number as a string of decimal digits.  The INTDEC_xxx flags
64
 *   specify how the number is to be formatted.` 
65
 */
66
intToDecimal(val, flags)
67
{
68
    local str;
69
    local sep;
70
71
    /* perform the basic conversion */
72
    str = toString(val);
73
74
    /* add group separators as needed */
75
    if ((flags & INTDEC_GROUP_COMMA) != 0)
76
    {
77
        /* explicitly use a comma as a separator */
78
        sep = ',';
79
    }
80
    else if ((flags & INTDEC_GROUP_PERIOD) != 0)
81
    {
82
        /* explicitly use a period as a separator */
83
        sep = '.';
84
    }
85
    else if ((flags & INTDEC_GROUP_SEP) != 0)
86
    {
87
        /* use the current languageGlobals separator */
88
        sep = languageGlobals.digitGroupSeparator;
89
    }
90
    else
91
    {
92
        /* no separator */
93
        sep = nil;
94
    }
95
96
    /* if there's a separator, add it in */
97
    if (sep != nil)
98
    {
99
        local i;
100
        local len;
101
        
102
        /* 
103
         *   Insert the separator before each group of three digits.
104
         *   Start at the right end of the string and work left: peel off
105
         *   the last three digits and insert a comma.  Then, move back
106
         *   four characters through the string - another three-digit
107
         *   group, plus the comma we inserted - and repeat.  Keep going
108
         *   until the amount we'd want to peel off the end is as long or
109
         *   longer than the entire remaining string. 
110
         */
111
        for (i = 3, len = str.length() ; len > i ; i += 4)
112
        {
113
            /* insert this comma */
114
            str = str.substr(1, len - i) + sep + str.substr(len - i + 1);
115
116
            /* note the new length */
117
            len = str.length();
118
        }
119
    }
120
121
    /* return the result */
122
    return str;
123
}
124
125
126
/* ------------------------------------------------------------------------ */
127
/*
128
 *   Convert an integer number to Roman numerals.  Returns a string with
129
 *   the Roman numeral format.  This can only accept numbers in the range
130
 *   1 to 4999; returns nil for anything outside of this range.  
131
 */
132
intToRoman(val)
133
{
134
    local str;
135
    local info =
136
    [
137
        /* numeral value / corresponding string */
138
        1000, 'M',
139
        900,  'CM',
140
        500,  'D',
141
        400,  'CD',
142
        100,  'C',
143
        90,   'XC',
144
        50,   'L',
145
        40,   'XL',
146
        10,   'X',
147
        9,    'IX',
148
        5,    'V',
149
        4,    'IV',
150
        1,    'I'
151
    ];
152
    local i;
153
154
    /* if the value is outside of the legal range, fail immediately */
155
    if (val < 1 || val > 4999)
156
        return nil;
157
158
    /* 
159
     *   iterate over the specifiers and apply each one as many times as
160
     *   possible 
161
     */
162
    for (str = '', i = 1 ; val != 0 ; )
163
    {
164
        /* 
165
         *   If we're greater than the current specifier, apply it;
166
         *   otherwise, move on to the next specifier. 
167
         */
168
        if (val >= info[i])
169
        {
170
            /* add this specifier's roman numeral into the result */
171
            str += info[i+1];
172
173
            /* subtract the corresponding value */
174
            val -= info[i];
175
        }
176
        else
177
        {
178
            /* move to the next specifier */
179
            i += 2;
180
        }
181
    }
182
183
    /* return the result */
184
    return str;
185
}
186