1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
200.
201.
202.
203.
204.
205.
206.
207.
208.
209.
210.
211.
212.
213.
214.
215.
216.
217.
218.
219.
220.
221.
222.
223.
224.
225.
226.
227.
228.
229.
230.
231.
232.
233.
234.
235.
236.
237.
238.
239.
240.
241.
242.
243.
244.
245.
246.
247.
248.
249.
250.
251.
252.
253.
254.
255.
256.
257.
258.
259.
260.
261.
262.
263.
264.
265.
266.
267.
268.
269.
270.
271.
272.
273.
274.
275.
276.
277.
278.
279.
280.
281.
282.
283.
284.
285.
286.
287.
288.
289.
290.
291.
292.
293.
294.
295.
296.
297.
298.
299.
300.
301.
302.
303.
304.
305.
306.
307.
308.
309.
310.
311.
312.
313.
314.
315.
316.
317.
318.
319.
320.
321.
322.
323.
324.
325.
326.
327.
328.
329.
330.
331.
332.
333.
334.
335.
336.
337.
338.
339.
340.
341.
342.
343.
344.
345.
346.
347.
348.
349.
350.
351.
352.
353.
354.
355.
356.
357.
358.
359.
360.
361.
362.
363.
364.
365.
366.
367.
368.
369.
370.
371.
372.
373.
374.
375.
376.
377.
378.
379.
380.
381.
382.
383.
384.
385.
386.
387.
388.
389.
390.
391.
392.
393.
394.
395.
396.
397.
398.
399.
400.
401.
402.
403.
404.
405.
406.
407.
408.
409.
410.
411.
412.
413.
414.
415.
416.
417.
418.
419.
420.
421.
422.
423.
424.
425.
426.
427.
428.
429.
430.
431.
432.
433.
434.
435.
436.
437.
438.
439.
440.
441.
442.
443.
444.
445.
446.
447.
448.
449.
450.
451.
452.
453.
454.
455.
456.
457.
458.
459.
460.
461.
462.
463.
464.
465.
466.
467.
468.
469.
470.
471.
472.
473.
474.
475.
476.
477.
478.
479.
480.
481.
482.
483.
484.
485.
486.
487.
488.
FUNCTION GF_ntow &&ords
*возвращает число прописью (словами)
PARAMETERS m.number,m.gender,;
m.dimens_1_name,m.dimens_2_name,m.dimens_3_name
IF EMPTY(m.number).OR.TYPE([m.number])#[N]
m.number= 1
ENDIF
IF EMPTY(m.gender).OR.TYPE([m.gender])#[N]
m.gender= 1
ENDIF
IF (EMPTY(m.dimens_1_name).OR.TYPE([m.dimens_1_name])#[C]).OR.;
(EMPTY(m.dimens_2_name).OR.TYPE([m.dimens_2_name])#[C]).OR.;
(EMPTY(m.dimens_3_name).OR.TYPE([m.dimens_3_name])#[C])
m.dimens_1_name=[РУБЛЬ]
m.dimens_2_name=[РУБЛЯ]
m.dimens_3_name=[РУБЛЕЙ]
ENDIF
*m.number - число, которое будет переведен в пропись
*m.gender - род единицы измерения: 1 -мужской, 2 -женский, 3 -средний
*m.dimens_1_name,m.dimens_2_name,m.dimens_3_name -
*наименования единицы измерения числа в двух падежах,
*например РУБЛЬ, РУБЛЯ, РУБЛЕЙ или
*ДОЛЛАР, ДОЛЛАРА, ДОЛЛАРОВ или
*МАРКА, МАРКИ, МАРОК или
*ТЕНЬГЕ, ТЕНЬГЕ, ТЕНЬГЕ
*___________________
*объявляем и инициализируем массив наименований для разряда сотен
*
*так как нулевой элемент массива отсутствует (он был бы уместен,
*когда количество сотен в трехразрядной группе равно 0 ), то массив
*заполняется как бы со сдвигом на единицу и адресация элементов
*производится по принципу число, стоящее в разряде сотен, плюс 1
PRIVATE hundred_ar
DIMENSION hundred_ar( 10 )
hundred_ar( 1 ) =[]
hundred_ar( 2 ) =[сто]
hundred_ar( 3 ) =[двести]
hundred_ar( 4 ) =[триста]
hundred_ar( 5 ) =[четыреста]
hundred_ar( 6 ) =[пятьсот]
hundred_ar( 7 ) =[шестьсот]
hundred_ar( 8 ) =[семьсот]
hundred_ar( 9 ) =[восемьсот]
hundred_ar( 10 )=[девятьсот]
*___________________
*объявляем и инициализируем массив наименований для разряда десятков
*
*здесь сдвиг на единицу не нужен, так как наименование десятка,
*соответствующее числу 10 , включено в массив наименований чисел от
* 1 до 19
PRIVATE ten_ar
DIMENSION ten_ar( 9 )
ten_ar( 1 ) =[]
ten_ar( 2 ) =[двадцать]
ten_ar( 3 ) =[тридцать]
ten_ar( 4 ) =[сорок]
ten_ar( 5 ) =[пятьдесят]
ten_ar( 6 ) =[шестьдесят]
ten_ar( 7 ) =[семьдесят]
ten_ar( 8 ) =[восемьдесят]
ten_ar( 9 ) =[девяносто]
*___________________
*объявляем и инициализируем массив наименований для чисел от 1 до 19
PRIVATE hundred_ar,ten_ar,_1to19_ar
DIMENSION _1to19_ar( 19 , 3 )
_1to19_ar( 1 , 1 )=[один] && для ед.изм. или ед. разрядов мужского рода
_1to19_ar( 1 , 2 )=[одна] && для ед.изм. или ед. разрядов женского рода
_1to19_ar( 1 , 3 )=[одно] && для ед.изм. или ед. разрядов среднего рода
_1to19_ar( 2 , 1 )=[два] && для ед.изм. или ед. разрядов мужского рода
_1to19_ar( 2 , 2 )=[две] && для ед.изм. или ед. разрядов женского рода
_1to19_ar( 2 , 3 )=[два] && для ед.изм. или ед. разрядов среднего рода
*далее - для единиц измерения любого рода
STORE [три] TO _1to19_ar( 03 , 1 )
STORE [четыре] TO _1to19_ar( 04 , 1 )
STORE [пять] TO _1to19_ar( 05 , 1 )
STORE [шесть] TO _1to19_ar( 06 , 1 )
STORE [семь] TO _1to19_ar( 07 , 1 )
STORE [восемь] TO _1to19_ar( 08 , 1 )
STORE [девять] TO _1to19_ar( 09 , 1 )
STORE [десять] TO _1to19_ar( 10 , 1 )
STORE [одиннадцать] TO _1to19_ar( 11 , 1 )
STORE [двенадцать] TO _1to19_ar( 12 , 1 )
STORE [тринадцать] TO _1to19_ar( 13 , 1 )
STORE [четырнадцать] TO _1to19_ar( 14 , 1 )
STORE [пятнадцать] TO _1to19_ar( 15 , 1 )
STORE [шестнадцать] TO _1to19_ar( 16 , 1 )
STORE [семнадцать] TO _1to19_ar( 17 , 1 )
STORE [восемнадцать] TO _1to19_ar( 18 , 1 )
STORE [девятнадцать] TO _1to19_ar( 19 , 1 )
PRIVATE m.cycle,m.cycle1
FOR m.cycle= 3 TO 19
STORE _1to19_ar(m.cycle, 1 ) TO _1to19_ar(m.cycle, 2 ),;
_1to19_ar(m.cycle, 3 )
ENDFOR
*теперь во всех строках массива наименований чисел от 1 до 19 ,
*начиная с третьей, все три элемента имеют одинаковые значения
*- для любого рода единиц разрядов или единицы измерения
*___________________
*объявляем и инициализируем основной массив, в котором будет
*монтироваться "прописное" представление числа
PRIVATE montage_ar
DIMENSION montage_ar( 5 , 5 )
STORE [] TO montage_ar
*___________________
*объявляем и инициализируем переменные для более удобной адресации
*столбцов основного массива
PRIVATE m.section_col,m.hundred_col,m.ten_col,m.one_col,m.dimens_col
m.section_col= 1
*столбец трехразрядных групп числа (строкового типа)
m.hundred_col= 2
*столбец наименований разряда сотен
m.ten_col= 3
*столбец наименований разряда десятков или чисел от 1 до 19
m.one_col= 4
*столбец наименований разряда единиц (или здесь пустая строка,
*если последние два разряда из трех дали число, лежащее в
*интервале с 1 по 19 , то соответствующее наименование размещается
*в предыдущем столбце
m.dimens_col = 5
*столбец наименований единицы разряда (для первых четырех строк
*массива) или единицы измерения самого числа (для пятой строки)
*____________________
*объявляем и инициализируем массив наименований единиц измерения
*разрядов (это в разных формах - триллион, миллиард, миллион, тысяча)
*и наименований единицы измерения самого числа - это в разных формах
*рубль\доллар\марка - само название в трех формах "берется" из
*последних трех переданных в функцию GF_ntow() параметров
*подобный подход основан на простом соображении - если, например,
*[миллион] (в разных формах) есть наименование единицы разрядов,
*соответствующей третьей трехразрядной группе числа, [тысяча](в
*разных формах) - четвертой группе, то наименование единицы измерения
*самого числа следует рассматривать так же как и такое же (по
*логическому назначению, а не по форме) наименование единицы
*измерения, соответствующей последней (пятой) группе числа
PRIVATE dimens_ar
DIMENSION dimens_ar( 5 , 3 )
dimens_ar( 1 , 1 )=[триллион]
dimens_ar( 1 , 2 )=[триллиона]
dimens_ar( 1 , 3 )=[триллионов]
dimens_ar( 2 , 1 )=[миллиард]
dimens_ar( 2 , 2 )=[миллиарда]
dimens_ar( 2 , 3 )=[миллиардов]
dimens_ar( 3 , 1 )=[миллион]
dimens_ar( 3 , 2 )=[миллиона]
dimens_ar( 3 , 3 )=[миллионов]
dimens_ar( 4 , 1 )=[тысяча]
dimens_ar( 4 , 2 )=[тысячи]
dimens_ar( 4 , 3 )=[тысяч]
dimens_ar( 5 , 1 )=m.dimens_1_name
dimens_ar( 5 , 2 )=m.dimens_2_name
dimens_ar( 5 , 3 )=m.dimens_3_name
*все! с объявлениями и присвоениями закончили, начинаем работу
m.number=STRTRAN(STR(m.number, 15 ),[ ],[ 0 ])
*преобразуем число в строковое представление; обязательная длина -
* 15 символов (на 5 трехразрядных групп)
*внимание! после операции STR(m.number, 15 ) строка дополняется
*пробелами СПЕРЕДИ, именно поэтому далее в цикле можно пользоваться
*SUBSTR через три символа, а не RIGHT через три символа
FOR m.cycle= 0 TO 4
montage_ar(m.cycle+ 1 ,m.section_col)=SUBSTR(m.number,m.cycle* 3 + 1 , 3 )
ENDFOR
*"нарезаем" строку, состоящую только из цифр, на 5 трехразрядных
*групп, кажду группу размещаем по строкам в элементы первого столбца
*основного массива первая группа - в первой строке, вторая - во
*второй, третья в третьей и т.п.
FOR m.cycle= 1 TO 5
DO GF_ntow1 WITH m.cycle
ENDFOR
*проходим по всем строкам массива, вызывая для каждой процедуру
*заполнения элементов во втором-пятом столбцах
IF EMPTY(montage_ar( 5 , 5 ))
montage_ar( 5 , 5 )=dimens_ar( 5 , 3 )
ENDIF
*если в элементе основного массива отсутствует наименование единицы
*измерения числа (это возможно, если последняя трехразрядная группа
*числа содержит только нули), то нам необходимо включить это
*наименование; у любой трехразрядной группы наименование может
*отсутствовать, но только не у последней! - наименование единицы
*измерения должно быть всегда; очевидно, что в этом случае не нужно
*думать о том, в какой форме должно быть это наименование - всегда
*во множественном числе (рублей, марок и т.п.)
PRIVATE m.return_mean,m.cycle1
*m.return_mean - переменная, в которую будет прибавляться строковое
*представление числа, значение этой переменной функция вернет в
*вызвавшую программу
*m.cycle1 - параметр для еще одного (вложенного) цикла
m.return_mean=[]
*начальное значение строки
FOR m.cycle= 1 TO 5
FOR m.cycle1= 2 TO 5
m.return_mean=m.return_mean+montage_ar(m.cycle,m.cycle1)+[ ]
ENDFOR
ENDFOR
*последовательно проходим по всем элементам основного массива
*кроме элементов первого столбца и прибавляем их строковые значения
*в итоговую строку, разделяя пробелами
m.return_mean=ALLTRIM(m.return_mean)
m.return_mean=STRTRAN(m.return_mean,[ ],[ ])
m.return_mean=STRTRAN(m.return_mean,[ ],[ ])
m.return_mean=STRTRAN(m.return_mean,[ ],[ ])
*из итоговой строки несколько раз удаляем двойные пробелы - в
*итоговой строке могут появиться даже тройные пробелы, так как
*при "складывании" итоговой строки пробелы между ее составляющими
*вставляются независимо от того, прибавляется ли пустая строка или
*в ней что-то есть, поэтому в строковых представлениях чисел,
*имеющих "в середине" много нулей, но по краям "обрамленные"
*цифрами, может быть много пробелов; пример такого числа
* 100 000 000 000 001 - сто триллионов один рубль
RETURN m.return_mean
*___________________
PROCEDURE GF_ntow1
*процедура вызывается для каждой строки основного массива
*в первом элементе каждой строки уже лежит строка -
*трехразрядная группа числа
*разбирая это строковое число на разряд сотен и т.д., процедура
*заполняет 2 - 4 элеметы нужной строки строковыми наименованиями
*чисел(разрядов), а в пятый элемент помещает наименование единицы
*разряда или (в пятый элемент пятой строки) - наименование
*единицы измерения самого числа
PARAMETERS m.row_in_ar
PRIVATE m.num_hundred
m.num_hundred=VAL(LEFT(montage_ar(m.row_in_ar,m.section_col), 1 ))
*из строки-трехразрядной группы, находящейся в первом элементе
*очередной строки основного массива выделяем разряд сотен и
*преобразуем полученную цифру в числовой тип (получаем количество
*сотен)
montage_ar(m.row_in_ar,m.hundred_col)=hundred_ar(m.num_hundred+ 1 )
*из массива hundred_ar берем значение, находящееся в элементе с
*индексом, равным количеству сотен плюс единица, теперь
*разряд сотен представлен прописью во втором столбце нужной строки
*массива montage_ar
IF !EMPTY(montage_ar(m.row_in_ar,m.hundred_col))
montage_ar(m.row_in_ar,m.dimens_col)=dimens_ar(m.row_in_ar, 3 )
ENDIF
*элементу, хранящему название разряда для текущей строки, сразу
*присваиваем это наименование из строки массива наименований
*разрядов - из третьей колонки, то есть для первой строки это
*будет [триллионов], для второй - [миллиардов] и т.п.
*то есть сразу предполагаем, что раз сотни этого разряда есть,
*(пока не важно, есть ли десятки и единицы), то название единицы
*измерения этого разряда (во множественном числе) уже можно указать
*в пятом элементе очередной строки основного массива
*далее, при работе с разрядами десятков и единиц, если они не
*содержат нули, это название в этом элементе придется заменить
*на соответствующее [триллион]-[триллиона] или на [миллиард]-
*-[миллиарда] и т.п.
*наименование попадает в пятый (m.dimens_col= 5 ) элемент очередной
*строки очередного массива
*___________________
*с разрядом сотен разобрались, теперь необходимо разобраться с
*разрядами десятков и единиц
PRIVATE m.num_ten_one
m.num_ten_one=VAL(SUBSTR(montage_ar(m.row_in_ar,m.section_col), 2 , 2 ))
*число, которое дают разряды десятков и единиц, получаем
*приведением к числовому типу последних двух цифр из трехразрадной
*группы
*___________________
*вся последующая работа нужна , только если число в этих двух
*разрядах больше нуля, так как только в этом случае необходимо
*формировать "прописное" представление числа и, возможно, изменять
*(в пятом элементе строки) наименование единицы разряда или единицы
*измерения; если же число в этих двух разрядах равно 0 , то ничего
*делать не надо
IF m.num_ten_one> 0
IF m.num_ten_one< 20
*если число в разрядах десятков и единиц находится в интервале с 1
*по 19 , то наименования чисел строятся по иному принципу, чем в
*случае, когда это число больше 19 (тогда отдельно нужно
*обрабатывать разряд десятков и разряд единиц)
montage_ar(m.row_in_ar,m.ten_col)=;
_1to19_ar(m.num_ten_one,GF_ntow2(m.row_in_ar))
*в столбце, предназначенном для хранения наименования числа
*разряда десятков, в элементе текущей строки помещаем "прописное"
*представление числа с 1 по 19 , при этом с помощью функции
*GF_ntow2() уточняем род наименования единицы разряда или род
*единицы измерения числа
montage_ar(m.row_in_ar,m.one_col)=[]
*при этом в элементе, предназначенном для хранения наименования
*числа в разряде единиц должна быть пустая строка, т.к. полное
*необходимое наименование для числа в интервале 0 <число< 20 уже
*размещено в предыдущем элементе этой же строки
ELSE
*значит разряды десятков и единиц дали число, большее 19 -
*необходимо отдельно разбираться с десятками и единицами
PRIVATE m.num_ten,m.num_one
m.num_ten=VAL(SUBSTR(montage_ar(m.row_in_ar,m.section_col), 2 , 1 ))
*количество десятков - вторая цифра из трехразрядной группы
m.num_one=VAL(SUBSTR(montage_ar(m.row_in_ar,m.section_col), 3 , 1 ))
*количество единиц - третья цифра из трехразрядной группы
IF m.num_ten> 0
*обратите внимание, что здесь m.num_ten никогда не будет равно
* 1 , так как с числами с 10 по 19 мы уже разобрались раньше,
*когда разбирались с числами с 1 по 19
montage_ar(m.row_in_ar,m.ten_col)=ten_ar(m.num_ten)
*если количество десятков больше 0 , то из массива наименований
*десятков берем соответсвующую строку
ENDIF
IF m.num_one> 0
*если количество единиц больше 1 , то из массива наименований
*чисел с 1 по 19 берем соответсвующую строку
montage_ar(m.row_in_ar,m.one_col)=;
_1to19_ar(m.num_one,GF_ntow2(m.row_in_ar))
*при этом с помощью функции GF_ntow2() уточняем род наименования
*единицы разряда или род единицы измерения числа
ENDIF
ENDIF
montage_ar(m.row_in_ar,m.dimens_col)=;
dimens_ar(m.row_in_ar,GF_ntow3(m.num_ten_one))
*когда проход по четырем элементам строки завершен, с помощью
*функции GF_ntow3() уточняем форму, в которой должно быть
*использовано наименование единицы разряда или наименование
*единицы измерения, в качестве параметра передаем число в разрядах
*десятков и единиц, а не отдельно число в разряде единиц
ENDIF
*___________________
FUNCTION GF_ntow2
PARAMETERS m.row_in_ar
*при прохождении четвертой и пятой строк массива необходимо
*разобраться с родом "единицы измерения" для соответствующей
*этой строке трехразрядной группы, так как в предыдущих трех
*строках единица измерения разряда мужского рода (триллион,
*миллиард, миллион), а в четвертой строке - женского рода
*(тысяча),
*а в пятой строке - она может быть любого рода (указывается
*вторым параметром (родом единицы измерения), переданным в
*функцию GF_ntow(), это переменная m.gender
*в зависимости от этого необходимо принять решение, какую форму
*использовать - один - одна - одно ; два - две - два
PRIVATE m.return_mean
DO CASE
CASE m.row_in_ar= 4
*четвертая строка массива соответствует трехрязрядной группе
*тысяч, поэтому род единицы разрядов - женский
m.return_mean= 2
*четвертая строка массива соответствует трехрязрядной группе
*тысяч, поэтому род единицы разрядов - женский, необходимо
*использовать элементы второго столбца массива _1to19_ar
CASE m.row_in_ar= 5
m.return_mean=m.gender
*пятая строка массива соответствует трехрязрядной группе
*единиц, поэтому род единицы измерения тот, который указан во
*втором параметре функции GF_ntow() ( 1 , 2 , 3 ), необходимо
*использовать элементы соответствующего столбца массива _1to19_ar
OTHERWISE
m.return_mean= 1
*во всех остальных случаях род единицы разрядов - мужской,
*соответственно должны использоваться элементы первого столбца
*массива _1to19_ar
ENDCASE
RETURN m.return_mean
*___________________
FUNCTION GF_ntow3
PARAMETERS m.num
*в зависимости от последнего разряда или, если последние два
*разряда дают число меньше 20 , то в зависимости от последних
*двух разрядов, необходимо определить форму наименования единицы
*измерения
*функция возвращает номер столбца dimens_ar, из которого должна
*быть "взята" форма наименования единицы разряда или единицы
*измерения
PRIVATE m.return_mean,m.num_one
IF m.num> 9 .AND.m.num< 20
*если число в двух последних разрядах попадает в этот интервал,
*то форма наименования единицы разряда или единицы измерения
*должна выбираться только на основе этого числа - из третьего
*столбца массива dimens_ar
m.return_mean= 3
ELSE
*форма наименования единицы разряда или единицы измерения
*должна выбираться только на основе числа, стоящего в разряде
*единиц
m.num_one=VAL(RIGHT(montage_ar(m.row_in_ar,m.section_col), 1 ))
DO CASE
CASE m.num_one= 1
m.return_mean= 1
CASE m.num_one> 1 .AND.m.num_one< 5
m.return_mean= 2
OTHERWISE
m.return_mean= 3
ENDCASE
ENDIF
if .f.
DO CASE
CASE m.num= 1
m.return_mean= 1
CASE m.num> 1 .AND.m.num< 5
m.return_mean= 2
CASE m.num> 5 .AND.m.num< 20
OTHERWISE
m.num_one=VAL(RIGHT(montage_ar(m.row_in_ar,m.section_col), 1 ))
DO CASE
CASE m.num_one= 1
m.return_mean= 1
CASE m.num_one> 1 .AND.m.num_one< 5
m.return_mean= 2
OTHERWISE
m.return_mean= 3
ENDCASE
ENDCASE
endif
RETURN m.return_mean