mirror of
https://scm.univ-tours.fr/22107988t/rappaurio-sae501_502.git
synced 2025-08-29 02:05:58 +02:00
1254 lines
37 KiB
JavaScript
1254 lines
37 KiB
JavaScript
/**
|
||
* @name CeL function for numeral systems
|
||
* @fileoverview 本檔案包含了記數系統用的 functions。
|
||
*
|
||
* @since
|
||
*
|
||
* @see <a href="https://en.wikipedia.org/wiki/List_of_numeral_systems"
|
||
* accessdate="2015/4/30 21:50">List of numeral systems</a>
|
||
*/
|
||
|
||
'use strict';
|
||
|
||
if (false) {
|
||
CeL.run('data.numeral', function() {
|
||
CeL.to_Chinese_numeral(1000);
|
||
});
|
||
}
|
||
|
||
// --------------------------------------------------------------------------------------------
|
||
|
||
// 不採用 if 陳述式,可以避免 Eclipse JSDoc 與 format 多縮排一層。
|
||
typeof CeL === 'function' && CeL.run({
|
||
// module name
|
||
name : 'data.numeral',
|
||
// data.native: .chars()
|
||
require : 'data.code.compatibility.|data.native.',
|
||
|
||
// 為了方便格式化程式碼,因此將 module 函式主體另外抽出。
|
||
code : module_code
|
||
});
|
||
|
||
function module_code(library_namespace) {
|
||
var
|
||
/** {Number}未發現之index。 const: 基本上與程式碼設計合一,僅表示名義,不可更改。(=== -1) */
|
||
NOT_FOUND = ''.indexOf('_');
|
||
// nothing required
|
||
|
||
/**
|
||
* null module constructor
|
||
*
|
||
* @class 處理記數系統的 functions
|
||
*/
|
||
var _// JSDT:_module_
|
||
= function() {
|
||
// null module constructor
|
||
};
|
||
|
||
/**
|
||
* for JSDT: 有 prototype 才會將之當作 Class
|
||
*/
|
||
_// JSDT:_module_
|
||
.prototype = {};
|
||
|
||
// -----------------------------------------------------------------------------------------------------------------
|
||
// 中文數字 (Chinese numerals)
|
||
|
||
function to_search_pattern(keys) {
|
||
var key, chars = [], long_keys = [];
|
||
function add(key) {
|
||
if (key)
|
||
if (key.length === 1)
|
||
chars.push(key);
|
||
else
|
||
long_keys.push(key);
|
||
}
|
||
|
||
if (Array.isArray(keys))
|
||
keys.forEach(add);
|
||
else
|
||
for (key in keys)
|
||
add(key);
|
||
|
||
chars = chars.length > 0 ? '[' + chars.join('') + ']' : '';
|
||
if (long_keys.length > 0 && chars)
|
||
long_keys.push(chars);
|
||
|
||
// /(?:long_keys|long_keys|[chars])/g
|
||
// /[chars]/g
|
||
return new RegExp(long_keys.length > 0 ? '(?:' + long_keys.join('|')
|
||
+ ')' : chars, 'g');
|
||
}
|
||
|
||
var
|
||
// 小寫數字
|
||
Chinese_numerals_Normal_digits = '〇一二三四五六七八九',
|
||
//
|
||
Chinese_numerals_Normal_digits_Array
|
||
//
|
||
= Chinese_numerals_Normal_digits.split(''),
|
||
//
|
||
Chinese_numerals_Normal_digits_pattern
|
||
//
|
||
= to_search_pattern(Chinese_numerals_Normal_digits_Array),
|
||
//
|
||
numerals_Normal_pattern = new RegExp('('
|
||
//
|
||
+ Chinese_numerals_Normal_digits_pattern.source + '|\\d+)', 'g'),
|
||
// 籌算: 步十百千萬
|
||
amount_pattern = new RegExp(numerals_Normal_pattern.source + '?([十百千])',
|
||
'g'),
|
||
|
||
// 正式大寫數字
|
||
Chinese_numerals_Formal_digits = '零壹貳參肆伍陸柒捌玖',
|
||
//
|
||
Chinese_numerals_Formal_digits_Array
|
||
//
|
||
= Chinese_numerals_Formal_digits.split(''),
|
||
//
|
||
Chinese_numerals_Formal_digits_pattern
|
||
//
|
||
= to_search_pattern(Chinese_numerals_Formal_digits_Array),
|
||
|
||
// http://thdl.ntu.edu.tw/suzhou/
|
||
// 蘇州碼子又稱花碼、番仔碼、草碼、菁仔碼
|
||
Suzhou_numerals_digits = '〇〡〢〣〤〥〦〧〨〩',
|
||
// Counting Rod Numerals As of Unicode version 8.0
|
||
Counting_rod_numerals_digits
|
||
// https://en.wikipedia.org/wiki/Counting_Rod_Numerals
|
||
= '𝍠𝍡𝍢𝍣𝍤𝍥𝍦𝍧𝍨𝍩𝍪𝍫𝍬𝍭𝍮𝍯𝍰𝍱',
|
||
// 全形阿拉伯數字 U+FF10~U+FF19 FULLWIDTH DIGIT
|
||
FULLWIDTH_DIGITS = '0123456789',
|
||
//
|
||
positional_Chinese_numerals_digits
|
||
//
|
||
= Chinese_numerals_Normal_digits
|
||
//
|
||
+ Chinese_numerals_Formal_digits
|
||
//
|
||
+ Suzhou_numerals_digits.slice(1) + FULLWIDTH_DIGITS,
|
||
//
|
||
positional_Chinese_numerals_digits_pattern
|
||
//
|
||
= new RegExp('[' + positional_Chinese_numerals_digits + ']', 'g'),
|
||
//
|
||
only_positional_Chinese_numerals_digits_pattern
|
||
//
|
||
= new RegExp('^[' + positional_Chinese_numerals_digits + ']+$'),
|
||
|
||
// 舊時/非正式/通用數字 正規化
|
||
numeral_convert_pair = {
|
||
// o : '〇',
|
||
O : '〇',
|
||
'○' : '〇',
|
||
弌 : '壹',
|
||
弍 : '貳',
|
||
兩 : '二',
|
||
叁 : '參',
|
||
叄 : '參',
|
||
弎 : '參',
|
||
亖 : '四',
|
||
// Firefox/3.0.19 無法 parse '䦉': 錯誤: invalid property id
|
||
'䦉' : '肆',
|
||
|
||
// [[ja:大字 (数字)]]
|
||
壱 : '壹',
|
||
弐 : '貳',
|
||
貮 : '貳',
|
||
参 : '參',
|
||
陆 : '陸',
|
||
// 去除常用字以防 false positive
|
||
// 漆 : '柒',
|
||
// 俗亦以「什」代拾,然易竄為「仟」。
|
||
// 什 : '拾',
|
||
|
||
// 念圓 : '貳拾圓',
|
||
// 念 : '貳拾',
|
||
廿 : '二十',
|
||
卄 : '二十',
|
||
卅 : '三十',
|
||
// http://www.bsm.org.cn/show_article.php?id=1888
|
||
// "丗五年" = "卅五年"
|
||
丗 : '三十',
|
||
// e.g., 卌又三年
|
||
卌 : '四十',
|
||
// 罕作「圩」
|
||
// 圩 : '五十',
|
||
皕 : '二百',
|
||
// 古亦作「陌」。
|
||
陌 : '佰',
|
||
// 古亦作「阡」。
|
||
阡 : '仟',
|
||
万 : '萬',
|
||
萬萬 : '億',
|
||
// 太常使用。
|
||
// 經 : '京',
|
||
杼 : '秭',
|
||
壤 : '穰',
|
||
|
||
厘 : '釐'
|
||
// 太常使用。
|
||
// 毛 : '毫'
|
||
},
|
||
//
|
||
numeral_convert_pattern,
|
||
|
||
// denomination, 萬進系統單位
|
||
// http://zh.wikipedia.org/wiki/%E4%B8%AD%E6%96%87%E6%95%B0%E5%AD%97
|
||
// http://zh.wikipedia.org/wiki/%E5%8D%81%E8%BF%9B%E5%88%B6
|
||
// http://zh.wikipedia.org/wiki/%E4%B8%AD%E6%96%87%E6%95%B0%E5%AD%97
|
||
// http://lists.w3.org/Archives/Public/www-style/2003Apr/0063.html
|
||
// http://forum.moztw.org/viewtopic.php?t=3043
|
||
// http://www.moroo.com/uzokusou/misc/suumei/suumei.html
|
||
// http://espero.51.net/qishng/zhao.htm
|
||
// http://www.nchu.edu.tw/~material/nano/newsbook1.htm
|
||
// http://www.moroo.com/uzokusou/misc/suumei/suumei1.html
|
||
// 十億(吉),兆(萬億),千兆(拍),百京(艾),十垓(澤),秭(堯),秭:禾予;溝(土旁);,無量大數→,無量,大數;[載]之後的[極]有的用[報]
|
||
// 異體:阿僧[禾氏],For Korean:阿僧祗;秭:禾予,抒,杼,For Korean:枾 For
|
||
// Korean:不可思議(不:U+4E0D→U+F967)
|
||
// Espana應該是梵文所譯
|
||
// 因為根據「大方廣佛華嚴經卷第四十五卷」中在「無量」這個數位以後還有無邊、無等、不可數、不可稱、不可思、不可量、不可說、不可說不可說,Espana應該是指上面其中一個..因為如果你有心查查Espana其實應該是解作西班牙文的「西班牙」
|
||
Chinese_numerals_Denominations
|
||
// ',萬,億,兆,京,垓,秭,穰,溝,澗,正,載,極,恒河沙,阿僧祇,那由他,不可思議,無量大數'
|
||
= ',萬,億,兆,京,垓,秭,穰,溝,澗,正,載,極',
|
||
//
|
||
Chinese_numerals_Denominations_Array
|
||
//
|
||
= Chinese_numerals_Denominations.split(','),
|
||
//
|
||
Chinese_numerals_Denominations_pattern
|
||
//
|
||
= to_search_pattern(Chinese_numerals_Denominations_Array),
|
||
//
|
||
Chinese_numerals_token_pattern
|
||
//
|
||
= new RegExp('(.*?)('
|
||
//
|
||
+ Chinese_numerals_Denominations_pattern.source + ')', 'g'),
|
||
|
||
// TODO:
|
||
// http://zh.wikipedia.org/wiki/%E5%8D%81%E9%80%80%E4%BD%8D
|
||
// 比漠微細的,是自天竺的佛經上的數字。而這些「佛經數字」已成為「古代用法」了。
|
||
// 小數單位(十退位):分,釐(厘),毫(毛),絲(秒),忽,微,纖,沙,塵(納),埃,渺,漠(皮),模糊,逡巡,須臾(飛),瞬息,彈指,剎那(阿),六德(德),虛,空,清,淨
|
||
// or:,虛,空,清,淨→,空虛,清淨(仄),阿賴耶,阿摩羅,涅槃寂靜(攸)
|
||
// 六釐英金庚款公債條例: 年息定為?釐, 年利率?厘
|
||
Chinese_numerals_Decimal_denominations = '分釐毫絲忽微纖沙塵埃渺漠',
|
||
//
|
||
numerals_Decimal_token_pattern
|
||
//
|
||
= new RegExp(numerals_Normal_pattern.source
|
||
//
|
||
+ '([' + Chinese_numerals_Decimal_denominations + '])', 'g'),
|
||
|
||
// 下數系統單位
|
||
Chinese_numerals_Normal_base_denomination
|
||
// 籌算: 步十百千萬
|
||
= (',十,百,千' + Chinese_numerals_Denominations).split(','),
|
||
//
|
||
Chinese_numerals_Formal_base_denomination
|
||
//
|
||
= (',拾,佰,仟' + Chinese_numerals_Denominations).split(','),
|
||
//
|
||
Chinese_numerals_Normal_pattern = new RegExp('(?:負?(?:['
|
||
+ Chinese_numerals_Normal_digits + '\\d ]['
|
||
+ Chinese_numerals_Normal_base_denomination.join('') + ']*|['
|
||
+ Chinese_numerals_Normal_base_denomination.join('')
|
||
+ ']+)+(又|分之)?)+', 'g'),
|
||
//
|
||
Chinese_numerals_Normal_Full_matched = new RegExp('^(?:負?['
|
||
+ Chinese_numerals_Normal_digits + '\\d '
|
||
+ Chinese_numerals_Normal_base_denomination.join('') + '又]+|分之)+$'),
|
||
//
|
||
numeral_value = Object.create(null);
|
||
|
||
_.Chinese_numerals_Normal_digits = Chinese_numerals_Normal_digits;
|
||
_.Chinese_numerals_Formal_digits = Chinese_numerals_Formal_digits;
|
||
_.Chinese_numerals_Denominations
|
||
//
|
||
= Chinese_numerals_Denominations_Array.join('');
|
||
|
||
(function() {
|
||
var base, scale = 0;
|
||
Chinese_numerals_Normal_digits_Array
|
||
//
|
||
.forEach(function(digits) {
|
||
numeral_value[digits] = scale;
|
||
scale++;
|
||
});
|
||
|
||
base = scale;
|
||
'十,百,千'.split(',')
|
||
// 籌算: 步十百千萬
|
||
.forEach(function(denomination) {
|
||
numeral_value[denomination] = scale;
|
||
scale *= base;
|
||
});
|
||
|
||
base = scale;
|
||
Chinese_numerals_Denominations_Array
|
||
//
|
||
.forEach(function(denomination) {
|
||
if (denomination) {
|
||
numeral_value[denomination] = scale;
|
||
scale *= base;
|
||
}
|
||
});
|
||
|
||
scale = .1;
|
||
Chinese_numerals_Decimal_denominations.split('')
|
||
//
|
||
.forEach(function(denomination) {
|
||
if (denomination) {
|
||
numeral_value[denomination] = scale;
|
||
scale /= 10;
|
||
}
|
||
});
|
||
|
||
for (scale = 1;
|
||
//
|
||
scale < Suzhou_numerals_digits.length; scale++) {
|
||
base = Suzhou_numerals_digits.charAt(scale);
|
||
numeral_value[base] = scale;
|
||
numeral_convert_pair[base]
|
||
//
|
||
= Chinese_numerals_Normal_digits[scale];
|
||
}
|
||
|
||
for (scale = 0;
|
||
//
|
||
scale < FULLWIDTH_DIGITS.length; scale++) {
|
||
base = FULLWIDTH_DIGITS.charAt(scale);
|
||
numeral_value[base] = scale;
|
||
numeral_convert_pair[base]
|
||
//
|
||
= Chinese_numerals_Normal_digits[scale];
|
||
}
|
||
|
||
numeral_convert_pattern
|
||
//
|
||
= to_search_pattern(numeral_convert_pair);
|
||
})();
|
||
|
||
// 對所有非正規之數字。
|
||
// TODO (bug): 十廿, 二廿
|
||
function normalize_Chinese_numeral(number_String) {
|
||
return number_String
|
||
// .replace(/\s+/g, '')
|
||
//
|
||
.replace(numeral_convert_pattern, function($0) {
|
||
return numeral_convert_pair[$0];
|
||
});
|
||
}
|
||
|
||
_.normalize_Chinese_numeral = normalize_Chinese_numeral;
|
||
|
||
function Chinese_numerals_Formal_to_Normal(number_String) {
|
||
return number_String.replace(Chinese_numerals_Formal_digits_pattern,
|
||
function($0) {
|
||
return Chinese_numerals_Normal_digits
|
||
//
|
||
.charAt(Chinese_numerals_Formal_digits.indexOf($0));
|
||
})
|
||
//
|
||
.replace(/[拾佰仟]/g, function(denomination) {
|
||
return '十百千'.charAt('拾佰仟'.indexOf(denomination));
|
||
});
|
||
}
|
||
|
||
_.Chinese_numerals_Formal_to_Normal
|
||
//
|
||
= Chinese_numerals_Formal_to_Normal;
|
||
|
||
function Chinese_numerals_Normal_to_Formal(number_String) {
|
||
return number_String.replace(Chinese_numerals_Normal_digits_pattern,
|
||
function($0) {
|
||
return Chinese_numerals_Formal_digits
|
||
//
|
||
.charAt(Chinese_numerals_Normal_digits.indexOf($0));
|
||
})
|
||
//
|
||
.replace(/[十百千]/g, function($0) {
|
||
return '拾佰仟'.charAt('十百千'.indexOf($0));
|
||
});
|
||
|
||
}
|
||
|
||
_.Chinese_numerals_Normal_to_Formal
|
||
//
|
||
= Chinese_numerals_Normal_to_Formal;
|
||
|
||
/**
|
||
* 將漢字中文數字轉換為半形阿拉伯數字表示法(小數系統 0-99999)
|
||
*
|
||
* @deprecated use from_Chinese_numeral.
|
||
*/
|
||
function deprecated_from_Chinese_numeral(number_String) {
|
||
if (!number_String || !isNaN(number_String))
|
||
return number_String;
|
||
|
||
number_String = Chinese_numerals_Formal_to_Normal(
|
||
//
|
||
normalize_Chinese_numeral('' + number_String));
|
||
|
||
var i = 0, l, m,
|
||
//
|
||
n = Chinese_numerals_Normal_digits_Array,
|
||
// 籌算: 萬千百十步
|
||
d = '萬千百十'.split(''), r = 0,
|
||
/**
|
||
* @see <a
|
||
* href="http://zh.wikipedia.org/wiki/%E6%97%A5%E8%AA%9E%E6%95%B8%E5%AD%97"
|
||
* accessdate="2012/9/10 21:0">日語數字</a>
|
||
*/
|
||
p = ('' + number_String).replace(/\s/g, '')
|
||
//
|
||
.replace(/[O○]/g, '〇');
|
||
for (; i < n.length; i++)
|
||
n[n[i]] = i;
|
||
for (i = 0; i < d.length; i++) {
|
||
if (p && NOT_FOUND !==
|
||
//
|
||
(m = d[i] ? p.indexOf(d[i]) : p.length))
|
||
if (!m && d[i] === '十')
|
||
r += 1, p = p.slice(1);
|
||
else if (isNaN(l = n[
|
||
//
|
||
p.slice(0, m).replace(/^〇+/, '')]))
|
||
return number_String;
|
||
else
|
||
r += l, p = p.slice(m + 1);
|
||
if (d[i])
|
||
r *= 10;
|
||
}
|
||
|
||
return r;
|
||
}
|
||
|
||
// More examples: see /_test suite/test.js
|
||
|
||
function from_positional_Chinese_numeral(number_String) {
|
||
return isNaN(number_String = number_String.replace(
|
||
positional_Chinese_numerals_digits_pattern, function(digit) {
|
||
return numeral_value[digit];
|
||
})) ? number_String : +number_String;
|
||
}
|
||
|
||
function to_positional_Chinese_numeral(number_String, formal) {
|
||
formal = formal ? Chinese_numerals_Formal_digits_Array
|
||
//
|
||
: Chinese_numerals_Normal_digits_Array;
|
||
return ('' + number_String)
|
||
//
|
||
.replace(/\d/g, function(digit) {
|
||
return formal[digit];
|
||
});
|
||
}
|
||
|
||
_.positional_Chinese_numerals_digits
|
||
//
|
||
= positional_Chinese_numerals_digits;
|
||
_.from_positional_Chinese_numeral
|
||
//
|
||
= from_positional_Chinese_numeral;
|
||
_.to_positional_Chinese_numeral
|
||
//
|
||
= to_positional_Chinese_numeral;
|
||
|
||
// 將漢字中文數字轉換為半形阿拉伯數字表示法。(正常情況下:小數系統 0-9999)
|
||
function from_Chinese_numeral_token(amount) {
|
||
if (!isNaN(amount))
|
||
return +amount;
|
||
|
||
// reset
|
||
amount_pattern.lastIndex = 0;
|
||
|
||
var token_sum = 0, matched, lastIndex = 0;
|
||
while (matched = amount_pattern.exec(amount)) {
|
||
lastIndex = amount_pattern.lastIndex;
|
||
// [ , digit, denomination ]
|
||
// for "一千零十一" 等。
|
||
token_sum += (matched[1]
|
||
//
|
||
&& matched[1] !== '〇' ? numeral_value[matched[1]] : 1)
|
||
//
|
||
* numeral_value[matched[2]];
|
||
}
|
||
|
||
// lastIndex 後面的全部放棄。
|
||
amount = amount.slice(lastIndex).replace(/^〇+/, '');
|
||
numerals_Normal_pattern.lastIndex = 0;
|
||
|
||
matched = numerals_Normal_pattern.exec(amount);
|
||
if (matched)
|
||
token_sum += isNaN(matched = matched[0])
|
||
//
|
||
? numeral_value[matched] : +matched;
|
||
|
||
return token_sum || 0;
|
||
}
|
||
|
||
/**
|
||
* 將漢字中文數字轉換為阿拉伯數字表示法。<br />
|
||
* 注意:本函數不會檢查 number_String 之正規與否!
|
||
*/
|
||
function from_Chinese_numeral(number_String) {
|
||
if (!number_String || !isNaN(number_String))
|
||
return number_String;
|
||
|
||
number_String = Chinese_numerals_Formal_to_Normal(
|
||
//
|
||
normalize_Chinese_numeral('' + number_String));
|
||
// console.log(Chinese_numerals_Normal_pattern);
|
||
// console.log(JSON.stringify(number_String));
|
||
|
||
if (!Chinese_numerals_Normal_Full_matched.test(number_String)) {
|
||
// 部分符合,僅針對符合部分處理。
|
||
Chinese_numerals_Normal_pattern.lastIndex = 0;
|
||
return number_String.replace(
|
||
//
|
||
Chinese_numerals_Normal_pattern, function($0) {
|
||
// console.log('-- ' + JSON.stringify($0));
|
||
// 避免前後空格被吃掉。
|
||
var token = $0.match(/^(\s*)(\S.*?)(\s*)$/);
|
||
if (!token) {
|
||
// 可能會是" "
|
||
return $0;
|
||
}
|
||
var digit = token[2].charAt(0);
|
||
token[2] = ('負十'.includes(digit)
|
||
|| positional_Chinese_numerals_digits.includes(digit)
|
||
|| (digit = token[2].charAt(1)) && ('十'.includes(digit)
|
||
//
|
||
|| positional_Chinese_numerals_digits.includes(digit))
|
||
// 不處理過大的位值,例如 "正"。
|
||
? from_Chinese_numeral(token[2]) : token[2]);
|
||
return token[1] + token[2] + token[3];
|
||
});
|
||
}
|
||
|
||
var sum = 0, lastIndex = 0,
|
||
//
|
||
negative = number_String.charAt(0) === '負',
|
||
//
|
||
matched = number_String
|
||
//
|
||
.match(/^(負)?(?:(.+)又)?(.+)分之(.+)$/);
|
||
if (matched) {
|
||
sum = (matched[2]
|
||
//
|
||
&& from_Chinese_numeral(matched[2]) || 0)
|
||
+ from_Chinese_numeral(matched[4])
|
||
/ from_Chinese_numeral(matched[3]);
|
||
return negative ? -sum : sum;
|
||
}
|
||
|
||
// reset
|
||
Chinese_numerals_token_pattern.lastIndex = 0;
|
||
|
||
// console.log([ number_String, Chinese_numerals_token_pattern ]);
|
||
while (matched = Chinese_numerals_token_pattern
|
||
//
|
||
.exec(number_String)) {
|
||
// [ , amount, denomination ]
|
||
// console.log(matched);
|
||
sum += from_Chinese_numeral_token(matched[1] || 1)
|
||
* numeral_value[matched[2]];
|
||
lastIndex = Chinese_numerals_token_pattern.lastIndex;
|
||
}
|
||
|
||
number_String = number_String.slice(lastIndex);
|
||
|
||
// reset
|
||
numerals_Decimal_token_pattern.lastIndex = 0;
|
||
|
||
// console.log([ sum, number_String, numerals_Decimal_token_pattern ]);
|
||
if (lastIndex = numerals_Decimal_token_pattern
|
||
//
|
||
.exec(number_String)) {
|
||
// 輸入 '捌佰3分' 之類。
|
||
// console.log(lastIndex);
|
||
lastIndex = lastIndex.index;
|
||
matched = [ , number_String.slice(0, lastIndex),
|
||
number_String.slice(lastIndex) ];
|
||
} else {
|
||
// 輸入 '捌佰3又3分' 之類。
|
||
matched = number_String.match(/(.*)[點又.](.*)/)
|
||
|| [ , number_String ];
|
||
}
|
||
|
||
if (false) {
|
||
console
|
||
.trace([ sum, matched, Chinese_numerals_Normal_Full_matched ]);
|
||
console.trace([ only_positional_Chinese_numerals_digits_pattern
|
||
.test(matched[1]) ]);
|
||
}
|
||
sum += only_positional_Chinese_numerals_digits_pattern.test(matched[1])
|
||
// e.g., CeL.from_Chinese_numeral('第一二三四章')
|
||
? from_positional_Chinese_numeral(matched[1])
|
||
: from_Chinese_numeral_token(matched[1]);
|
||
|
||
// console.trace(sum);
|
||
|
||
if (number_String = matched[2]) {
|
||
// 處理小數。
|
||
for (var base = .1, lastIndex = 0;; base /= 10) {
|
||
numerals_Decimal_token_pattern.lastIndex = lastIndex;
|
||
if (matched = numerals_Decimal_token_pattern
|
||
//
|
||
.exec(number_String)) {
|
||
lastIndex
|
||
//
|
||
= numerals_Decimal_token_pattern.lastIndex;
|
||
// 單位
|
||
base = numeral_value[matched[2]];
|
||
matched = matched[1];
|
||
} else {
|
||
numerals_Normal_pattern.lastIndex = lastIndex;
|
||
if (matched = numerals_Normal_pattern
|
||
//
|
||
.exec(number_String)) {
|
||
lastIndex
|
||
//
|
||
= numerals_Normal_pattern.lastIndex;
|
||
matched = matched[0];
|
||
} else
|
||
break;
|
||
}
|
||
if (isNaN(matched))
|
||
matched = numeral_value[matched];
|
||
else if (matched > 9)
|
||
matched = matched.replace(/^(\d)/, '$1.');
|
||
else
|
||
matched = +matched;
|
||
sum += matched * base;
|
||
}
|
||
}
|
||
|
||
return negative ? -sum : sum;
|
||
}
|
||
|
||
/**
|
||
* 將阿拉伯數字轉為中文數字<b>下數系統</b>大寫(Long scale)、小寫(Short scale)兩種表示法/中文數字讀法<br />
|
||
* 處理1-99999的數,尚有bug。
|
||
*/
|
||
function to_Chinese_numeral_Low_scale(number_String, formal) {
|
||
// 用r=[]約多花一倍時間!
|
||
var i = 0, r = '', l = number_String.length - 1, d,
|
||
//
|
||
tnum = formal ? Chinese_numerals_Formal_digits_Array
|
||
: Chinese_numerals_Normal_digits_Array,
|
||
//
|
||
zero = tnum[0],
|
||
//
|
||
tbd = formal ? Chinese_numerals_Formal_base_denomination
|
||
: Chinese_numerals_Normal_base_denomination;
|
||
|
||
for (; i <= l; i++)
|
||
// if(d=parseInt(number_String.charAt(i)))比較慢
|
||
if ((d = number_String.charAt(i)) !== '0')
|
||
// '〇一二三四五六七八'.charAt(d) 比較慢
|
||
r += tnum[d] + tbd[l - i];
|
||
else if (r.slice(-1) != zero)
|
||
if (Math.floor(number_String.slice(i + 1)))
|
||
r += zero;
|
||
else
|
||
break;
|
||
return r;
|
||
}
|
||
|
||
if (false)
|
||
(function() {
|
||
// 2.016,2.297,2.016
|
||
var d = new Date, v = '12345236', i = 0, a;
|
||
for (; i < 10000; i++)
|
||
a = to_Chinese_numeral(v);
|
||
alert(v + '\n→' + a + '\ntime:' + gDate(new Date - d));
|
||
});
|
||
|
||
/**
|
||
* 將阿拉伯數字轉為萬進中文數字表示法。 num>1京時僅會取概數,此時得轉成string再輸入! TODO: 統整:尚有bug。 廿卅 小數
|
||
*
|
||
* @param {Number}number
|
||
* native number
|
||
* @param {Boolean}[formal]
|
||
* kind
|
||
*
|
||
* @returns {String} 中文數字
|
||
*
|
||
*/
|
||
function to_Chinese_numeral(number, formal) {
|
||
// number = parseFloat(number);
|
||
number = (typeof number === 'number'
|
||
//
|
||
? number.toString(10)
|
||
//
|
||
: '' + number)
|
||
// 避免前後空格被吃掉。
|
||
// .replace(/[,\s]/g, '')
|
||
;
|
||
|
||
if (!/^[+\-]?(?:\d+(?:\.\d*)?|(?:\d*\.)?\d+)$/.test(number)) {
|
||
// 非數值
|
||
return number.replace(
|
||
//
|
||
/[+\-]?(?:\d+(?:\.\d*)?|(?:\d*\.)?\d+)/g, function($0) {
|
||
// 避免前後空格被吃掉。
|
||
var token = $0.match(/^(\s*)(\S.*?)(\s*)$/);
|
||
if (!token) {
|
||
// 可能會是" "
|
||
return $0;
|
||
}
|
||
// console.log(token);
|
||
return token[1] + to_Chinese_numeral(token[2], formal)
|
||
+ token[3];
|
||
});
|
||
}
|
||
|
||
var j,
|
||
// i:integer,整數;
|
||
i,
|
||
// d:decimal,小數
|
||
d = number.indexOf('.'), k, l, m, addZero = false,
|
||
//
|
||
tnum = formal ? Chinese_numerals_Formal_digits_Array
|
||
//
|
||
: Chinese_numerals_Normal_digits_Array,
|
||
//
|
||
zero = tnum[0];
|
||
if (d === NOT_FOUND)
|
||
d = 0;
|
||
else
|
||
for (number = number.replace(/0+$/, ''),
|
||
//
|
||
i = number.slice(d + 1),
|
||
//
|
||
number = number.slice(0, d),
|
||
//
|
||
d = '', j = 0; j < i.length; j++)
|
||
// 小數
|
||
d += tnum[i.charAt(j)];
|
||
|
||
// 至此 number 為整數。
|
||
if (number.charAt(0) === '-')
|
||
i = '負', number = number.slice(1);
|
||
else
|
||
i = '';
|
||
number = number.replace(/^0+/, '');
|
||
|
||
m = number.length % 4, j = m - 4, l = (number.length - (m || 4)) / 4;
|
||
// addZero=false, l=Math.floor((number.length-1)/4)
|
||
for (; j < number.length; m = 0, l--)
|
||
// 這邊得用 parseInt( ,10):
|
||
// parseInt('0~')會用八進位,其他也有奇怪的效果。
|
||
if (Math.floor(m = m ? number.slice(0, m) : number
|
||
.substr(j += 4, 4))) {
|
||
m = to_Chinese_numeral_Low_scale(m, formal);
|
||
if (addZero = addZero && m.charAt(0) != zero) {
|
||
i += zero + m
|
||
//
|
||
+ Chinese_numerals_Denominations_Array[l];
|
||
addZero = false;
|
||
} else
|
||
i += m
|
||
//
|
||
+ Chinese_numerals_Denominations_Array[l];
|
||
} else
|
||
addZero = true;
|
||
|
||
// 習慣用法: 一十 → 十
|
||
return (i ? i.replace(/^(負)?[一壹]([十拾])/, '$1$2') : zero)
|
||
+ (d ? '點' + d : '');
|
||
}
|
||
|
||
_.from_Chinese_numeral = from_Chinese_numeral;
|
||
_.to_Chinese_numeral = to_Chinese_numeral;
|
||
|
||
/**
|
||
* 各區文化特色 - 貨幣轉換:<br />
|
||
* 轉換成新臺幣中文大寫金額表示法。<br />
|
||
* Converted into money notation.
|
||
*
|
||
* @example <code>
|
||
|
||
// More examples: see /_test suite/test.js
|
||
|
||
* </code>
|
||
*
|
||
* @param {Number|String}amount
|
||
* 貨幣數量。
|
||
* @returns {String} 新臺幣金額中文大寫表示法。
|
||
*
|
||
* @requires to_Chinese_numeral()
|
||
*/
|
||
function to_TWD(amount) {
|
||
if (typeof amount === 'string')
|
||
amount = amount.replace(/[\s,$]+/g, '');
|
||
|
||
amount = to_Chinese_numeral(amount, true)
|
||
// 銀行習慣用法,零可以不用寫。
|
||
.replace(/([佰仟萬億兆京垓秭穰溝澗正載極])零/g, '$1')
|
||
// 100000 → 壹拾萬圓整
|
||
.replace(/^拾/, '壹拾');
|
||
|
||
// 大寫金額數字應緊接“人民幣/港幣/台幣”字樣填寫,不得留有空位。
|
||
return '新臺幣' + (amount.includes('點') ? amount.replace(
|
||
//
|
||
/點(.)(.)?(.)?/, function($0, $1, $2, $3) {
|
||
return '圓' + $1 + '角'
|
||
// 日本明治時代臺灣 1圓=100錢=1000厘, 不使用"零"這個數字
|
||
// e.g., "五百三圓二十三錢五厘"
|
||
+ ($2 ? $2 + '分' + ($3 ? $3 + '文' : '') : '');
|
||
}) :
|
||
// 在“元”(或“圓”)之後、應寫“整”(或“正”)字
|
||
// 在“角”之後,可以不寫“整”(或“正”)字
|
||
// 大寫金額數字有“分”的,“分”後面不寫“整”(或“正”)字。
|
||
amount + '圓整');
|
||
}
|
||
|
||
_// JSDT:_module_
|
||
.to_TWD = to_TWD;
|
||
|
||
/**
|
||
* Japanese numerals
|
||
*
|
||
* @param {Number}number
|
||
* native number
|
||
*
|
||
* @returns {String} Japanese numerals
|
||
*/
|
||
function to_Japanese_numeral(number) {
|
||
return to_Chinese_numeral(number).replace(/〇/g, '').replace(/萬/, '万');
|
||
}
|
||
|
||
_.to_Japanese_numeral = to_Japanese_numeral;
|
||
|
||
// https://en.wikipedia.org/wiki/Long_and_short_scales
|
||
// http://blog.functionalfun.net/2008/08/project-euler-problem-17-converting.html
|
||
var English_numerals = {
|
||
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",
|
||
1000000 : "million",
|
||
1000000000 : "billion",
|
||
1000000000000 : "trillion",
|
||
1000000000000000 : "quadrillion",
|
||
// Number.isSafeInteger(1000000000000000000) === false
|
||
'1000000000000000000' : "quintillion"
|
||
};
|
||
|
||
// @inner
|
||
function to_English_numeral_small(number) {
|
||
// assert: number = 1 ~ 999
|
||
// hundreds
|
||
var conversion = number / 100 | 0;
|
||
|
||
if (number %= 100)
|
||
if (number in English_numerals)
|
||
number = English_numerals[number];
|
||
else {
|
||
// units
|
||
var _1 = number % 10;
|
||
_1 = _1 ? English_numerals[_1] : '';
|
||
// tens
|
||
number = number / 10 | 0;
|
||
if (number) {
|
||
number = English_numerals[number * 10];
|
||
if (_1)
|
||
number += '-' + _1;
|
||
} else
|
||
number = _1;
|
||
}
|
||
|
||
if (conversion) {
|
||
conversion = English_numerals[conversion] + ' '
|
||
+ English_numerals[100];
|
||
if (number)
|
||
conversion += ' and ' + number;
|
||
} else
|
||
conversion = number;
|
||
return conversion;
|
||
}
|
||
|
||
// written out numbers in words. Get number name.
|
||
// British usage
|
||
// @see http://www.grammarbook.com/numbers/numbers.asp
|
||
function to_English_numeral(number) {
|
||
if (number != Math.floor(number)) {
|
||
library_namespace.error('Cannot conver [' + number + ']!');
|
||
}
|
||
|
||
number = Math.floor(number);
|
||
if (number < 0)
|
||
return "negative " + to_English_numeral(-number);
|
||
if (number < 91 && (number in English_numerals))
|
||
// for zero.
|
||
return English_numerals[number];
|
||
|
||
var base = 1000, unit = 1, conversion = [], remainder,
|
||
// remainder, 0 ~ 999 (1000-1)
|
||
small = number % base;
|
||
while (number = Math.floor(number / base)) {
|
||
unit *= base;
|
||
if (remainder = number % base)
|
||
conversion.unshift(to_English_numeral_small(remainder) + ' '
|
||
+ English_numerals[unit]);
|
||
}
|
||
|
||
if (conversion = conversion.join(', ')) {
|
||
if (small)
|
||
conversion += ' and '
|
||
//
|
||
+ to_English_numeral_small(small);
|
||
} else
|
||
conversion = small ? to_English_numeral_small(small) : '';
|
||
return conversion;
|
||
}
|
||
|
||
_.to_English_numeral = to_English_numeral;
|
||
|
||
// -----------------------------------------------------------------------------------------------------------------
|
||
// (十進位)位值直接轉換用
|
||
// https://en.wikipedia.org/wiki/Positional_notation
|
||
|
||
function convert_positional(digit_set, name) {
|
||
var digits;
|
||
if (typeof digit_set !== 'string' || 10 !==
|
||
//
|
||
(digits = digit_set.chars()).length) {
|
||
library_namespace.error('Invalid digits of [' + name + ']: ('
|
||
+ digits.length + ') [' + digit_set + ']');
|
||
return;
|
||
}
|
||
|
||
var PATTERN_numeral = new RegExp(
|
||
digit_set.length === digits.length ? '[' + digit_set + ']'
|
||
: digits.join('|'), 'g');
|
||
digits.forEach(function(digit, index) {
|
||
numeral_convert_pair[digit] = index;
|
||
});
|
||
|
||
/**
|
||
* native number → positional numeral system
|
||
*
|
||
* @param {Number}number
|
||
* native number
|
||
*
|
||
* @returns {String} specified numerals
|
||
*/
|
||
function to_numeral(number) {
|
||
return String(number).replace(/\d/g, function(digit) {
|
||
return digits[digit];
|
||
});
|
||
}
|
||
|
||
/**
|
||
* positional numeral system → native number
|
||
*
|
||
* @param {String}number
|
||
* specified numerals
|
||
*
|
||
* @returns {Number} native number
|
||
*/
|
||
to_numeral.from = function from_numeral(number) {
|
||
number = String(number).replace(PATTERN_numeral, function(digit) {
|
||
return numeral_convert_pair[digit];
|
||
});
|
||
if (!isNaN(number))
|
||
number = Number(number);
|
||
return number;
|
||
}
|
||
|
||
return to_numeral;
|
||
}
|
||
|
||
// http://wikimediafoundation.org/wiki/Template:ConvertDigit
|
||
// https://github.com/esetera/Objavi/blob/master/digits.txt
|
||
// https://de.wikipedia.org/wiki/Zahlzeichen_in_Unicode
|
||
// TODO: https://en.wiktionary.org/wiki/8
|
||
(function() {
|
||
var positional_digits = {
|
||
// Eastern Arabic numerals
|
||
// https://en.wikipedia.org/wiki/Eastern_Arabic_numerals
|
||
// 中東阿拉伯文數字, 標準阿拉伯文數字
|
||
// Western Arabic / Hindu–Arabic numeral system: 0123456789
|
||
// 在埃及,「二」通常用另一種寫法。
|
||
Arabic : '٠١٢٣٤٥٦٧٨٩',
|
||
// Perso-Arabic variant, Persian, Urdu, 東阿拉伯文數字
|
||
Perso : '۰۱۲۳۴۵۶۷۸۹',
|
||
|
||
Balinese : '᭐᭑᭒᭓᭔᭕᭖᭗᭘᭙',
|
||
|
||
// Bengali numerals (সংখ্যা shôngkhæ), 孟加拉文數字,
|
||
// Bengali-Assamese numerals
|
||
// https://en.wikipedia.org/wiki/Bengali_numerals
|
||
// ৴৵৶৷৸৹
|
||
Bangla : '০১২৩৪৫৬৭৮৯',
|
||
|
||
Brahmi : '𑁦𑁧𑁨𑁩𑁪𑁫𑁬𑁭𑁮𑁯',
|
||
Chakma : '𑄶𑄷𑄸𑄹𑄺𑄻𑄼𑄽𑄾𑄿',
|
||
Cham : '꩐꩑꩒꩓꩔꩕꩖꩗꩘꩙',
|
||
|
||
// 天城文(देवनागरी / devanāgarī)
|
||
// https://hi.wikipedia.org/wiki/%E0%A4%AE%E0%A5%80%E0%A4%A1%E0%A4%BF%E0%A4%AF%E0%A4%BE%E0%A4%B5%E0%A4%BF%E0%A4%95%E0%A4%BF:Gadget-Numeral_converter.js
|
||
// https://hi.wikipedia.org/wiki/%E0%A4%B5%E0%A4%BF%E0%A4%95%E0%A4%BF%E0%A4%AA%E0%A5%80%E0%A4%A1%E0%A4%BF%E0%A4%AF%E0%A4%BE:%E0%A4%85%E0%A4%82%E0%A4%95_%E0%A4%AA%E0%A4%B0%E0%A4%BF%E0%A4%B5%E0%A4%B0%E0%A5%8D%E0%A4%A4%E0%A4%95
|
||
Devanagari : '०१२३४५६७८९',
|
||
|
||
Gujarati : '૦૧૨૩૪૫૬૭૮૯',
|
||
// Gurmukhī numerals
|
||
// https://en.wikipedia.org/wiki/Gurmukh%C4%AB_alphabet#Numerals
|
||
Gurmukhi : '੦੧੨੩੪੫੬੭੮੯',
|
||
Javanese : '꧐꧑꧒꧓꧔꧕꧖꧗꧘꧙',
|
||
Kannada : '೦೧೨೩೪೫೬೭೮೯',
|
||
// Kayah Li
|
||
Kayah_Li : '꤀꤁꤂꤃꤄꤅꤆꤇꤈꤉',
|
||
|
||
// Khmer, Cambodian, 高棉文數字.
|
||
// https://km.wikipedia.org/wiki/%E1%9E%91%E1%9F%86%E1%9E%96%E1%9F%90%E1%9E%9A%E1%9E%82%E1%9F%86%E1%9E%9A%E1%9E%BC:Number_table_sorting
|
||
Khmer : '០១២៣៤៥៦៧៨៩',
|
||
|
||
// Tai Tham Hora 十進位數字系統。
|
||
Lanna : '᪀᪁᪂᪃᪄᪅᪆᪇᪈᪉',
|
||
// Tai Tham Tham 十進位數字系統。老傣文,又稱老傣仂文、蘭納文. Lanna script
|
||
Tai_Tham : '᪐᪑᪒᪓᪔᪕᪖᪗᪘᪙',
|
||
|
||
// 寮國/寮文數字
|
||
Lao : '໐໑໒໓໔໕໖໗໘໙',
|
||
Lepcha : '᱀᱁᱂᱃᱄᱅᱆᱇᱈᱉',
|
||
Limbu : '᥆᥇᥈᥉᥊᥋᥌᥍᥎᥏',
|
||
Malayalam : '൦൧൨൩൪൫൬൭൮൯',
|
||
// Meitei-Mayek
|
||
Meitei_Mayek : '꯰꯱꯲꯳꯴꯵꯶꯷꯸꯹',
|
||
Mongolian : '᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙',
|
||
// or Burmese. 緬甸文數字.
|
||
// 警告:其中非空!
|
||
Myanmar : '၀၁၂၃၄၅၆၇၈၉',
|
||
// 緬甸撣邦文十進位數字系統。
|
||
// 警告:其中非空!
|
||
Myanmar_Shan : '႐႑႒႓႔႕႖႗႘႙',
|
||
// Neu-Tai-Lue.
|
||
Neu_Tai_Lue : '᧐᧑᧒᧓᧔᧕᧖᧗᧘᧙',
|
||
// N'Ko, r to l
|
||
NKo : '߀߁߂߃߄߅߆߇߈߉',
|
||
Oriya : '୦୧୨୩୪୫୬୭୮୯',
|
||
// Ol Chiki decimal numeral system. 桑塔爾文十進位數字系統。
|
||
Ol_Chiki : '᱐᱑᱒᱓᱔᱕᱖᱗᱘᱙',
|
||
Osmanya : '𐒠𐒡𐒢𐒣𐒤𐒥𐒦𐒧𐒨𐒩',
|
||
Saurashtra : '꣐꣑꣒꣓꣔꣕꣖꣗꣘꣙',
|
||
Sharada : '𑇐𑇑𑇒𑇓𑇔𑇕𑇖𑇗𑇘𑇙',
|
||
// Sorang-Sompeng
|
||
Sorang_Sompeng : '𑃰𑃱𑃲𑃳𑃴𑃵𑃶𑃷𑃸𑃹',
|
||
Sundanese : '᮰᮱᮲᮳᮴᮵᮶᮷᮸᮹',
|
||
Takri : '𑛀𑛁𑛂𑛃𑛄𑛅𑛆𑛇𑛈𑛉',
|
||
// Tamil (Grantha), 泰米爾文數字
|
||
// https://www.adobe.com/type/browser/pdfs/1965.pdf
|
||
Tamil : '௦௧௨௩௪௫௬௭௮௯',
|
||
Telugu : '౦౧౨౩౪౫౬౭౮౯',
|
||
// 藏文數字
|
||
Tibetan : '༠༡༢༣༤༥༦༧༨༩',
|
||
// 泰文數字 th:ตัวเลขไทย
|
||
// https://th.wikipedia.org/wiki/%E0%B8%95%E0%B8%B1%E0%B8%A7%E0%B9%80%E0%B8%A5%E0%B8%82%E0%B9%84%E0%B8%97%E0%B8%A2
|
||
Thai : '๐๑๒๓๔๕๖๗๘๙',
|
||
Vai : '꘠꘡꘢꘣꘤꘥꘦꘧꘨꘩'
|
||
};
|
||
|
||
for ( var name in positional_digits) {
|
||
var to_numeral = convert_positional(positional_digits[name], name);
|
||
if (to_numeral) {
|
||
_['to_' + name + '_numeral'] = to_numeral;
|
||
_['from_' + name + '_numeral'] = to_numeral.from;
|
||
}
|
||
}
|
||
})();
|
||
|
||
// -----------------------------------------------------------------------------------------------------------------
|
||
// Roman numerals
|
||
// https://en.wikipedia.org/wiki/Roman_numerals
|
||
// https://en.wiktionary.org/wiki/Appendix:Roman_numerals
|
||
// TODO: to Alternative forms
|
||
var Roman_numeral_alternative = {
|
||
'ↅ' : 'VI',
|
||
'ↆ' : 'L',
|
||
// Safari 11: Invalid character
|
||
'Ⅼ' : 'L',
|
||
'Ⅽ' : 'C',
|
||
'Ⅾ' : 'D',
|
||
'Ⅿ' : 'M',
|
||
'ⅼ' : 'L',
|
||
'ⅽ' : 'C',
|
||
'ⅾ' : 'D',
|
||
'ⅿ' : 'M',
|
||
'ↀ' : 'M'
|
||
}, PATTERN_Roman_numeral_alternative,
|
||
//
|
||
Roman_numeral_pair = {},
|
||
//
|
||
PATTERN_Roman = [],
|
||
|
||
// assert: 2個一組為十進位。
|
||
Roman_numeral_value = 'IVXLCDMↁↂↇↈ'.split(''),
|
||
// Roman_numeral_value[apostrophus_starts] 開始為 apostrophus 表示法。
|
||
apostrophus_starts = Roman_numeral_value.indexOf('ↁ');
|
||
|
||
Roman_numeral_value.forEach(function(digit, index) {
|
||
var is_unit = index % 2 === 0, next;
|
||
Roman_numeral_pair[digit] = (is_unit ? 1 : 5)
|
||
* Math.pow(10, index / 2 | 0);
|
||
if (is_unit) {
|
||
var next = Roman_numeral_value[index + 1];
|
||
PATTERN_Roman.unshift('('
|
||
+ (next ? digit + '[' + next
|
||
+ Roman_numeral_value[index + 2] + ']|' + next
|
||
+ '?' : '') + digit + '*)');
|
||
}
|
||
});
|
||
|
||
// 千百十個: /(M*)(C[DM]|D?C*)(X[LC]|L?X*)(I[VX]|V?I*)/i
|
||
PATTERN_Roman = new RegExp(PATTERN_Roman.join(''), 'i');
|
||
// console.log(PATTERN_Roman);
|
||
// /(ↈ*)(ↂ[ↇↈ]|ↇ?ↂ*)(M[ↁↂ]|ↁ?M*)(C[DM]|D?C*)(X[LC]|L?X*)(I[VX]|V?I*)/i
|
||
|
||
// apostrophus: expressed in "apostrophus" notation.
|
||
function to_Roman_numeral(number, apostrophus) {
|
||
if (!(number > 0) || number != (number | 0)) {
|
||
/**
|
||
* the word nulla (the Latin word meaning "none") was used by
|
||
* medieval computists in lieu of 0.<br />
|
||
* About 725, Bede or one of his colleagues used the letter N, the
|
||
* initial of nulla, in a table of epacts, all written in Roman
|
||
* numerals.
|
||
*/
|
||
// return number === 0 ? 'N' : number;
|
||
return number;
|
||
}
|
||
|
||
/** {Natural}已處理的 Roman 數字。 */
|
||
var value = [],
|
||
/** {Natural}剩下尚未處理的數值。 */
|
||
left = number | 0;
|
||
|
||
// 將 apostrophus 轉成可接受的最大 index。
|
||
apostrophus = apostrophus ? Roman_numeral_value.length
|
||
: apostrophus_starts;
|
||
|
||
// index += 2: assert: 2個一組為十進位。
|
||
for (var index = 0; left > 0; index += 2) {
|
||
if (index >= apostrophus) {
|
||
library_namespace.error(
|
||
// OUT OF RANGE: number ≥ 1000000
|
||
'The number is too large to be expressed in Roman numerals: '
|
||
+ number);
|
||
return;
|
||
}
|
||
|
||
var digits,
|
||
/** {Integer}位值。 */
|
||
position = left % 10;
|
||
left = left / 10 | 0;
|
||
if ((position + 1) % 5 === 0 && apostrophus >
|
||
// position = 4 or 9 時之特殊處置。必須有此數字表示法,才允許通過。
|
||
(digits = index + (position === 4 ? 1 : 2))) {
|
||
digits = Roman_numeral_value[index]
|
||
+ Roman_numeral_value[digits];
|
||
} else {
|
||
if (position > 4
|
||
// [index + 1] 可能已經越界。
|
||
&& (digits = Roman_numeral_value[index + 1])) {
|
||
position -= 5;
|
||
} else {
|
||
digits = '';
|
||
}
|
||
digits += Roman_numeral_value[index].repeat(position);
|
||
}
|
||
value.push(digits);
|
||
}
|
||
|
||
return value.reverse().join('');
|
||
}
|
||
|
||
function Roman_position(previous, position) {
|
||
if (!position)
|
||
return previous;
|
||
|
||
if (position.length === 1)
|
||
return previous + Roman_numeral_pair[position];
|
||
|
||
var _1 = Roman_numeral_pair[position[0]],
|
||
//
|
||
_2 = Roman_numeral_pair[position[1]];
|
||
if (_2 > _1)
|
||
// assert: position.length === 2
|
||
return previous + _2 - _1;
|
||
|
||
return previous + _1 + _2 * (position.length - 1);
|
||
}
|
||
|
||
// TODO: 'Ↄ', 'ↄ'
|
||
function from_Roman_numeral(number) {
|
||
var matched = normalize_Roman_numeral(number).match(PATTERN_Roman);
|
||
|
||
return matched ? matched.slice(1).reduce(Roman_position, 0) : number;
|
||
}
|
||
|
||
function normalize_Roman_numeral(number) {
|
||
return String(number)
|
||
// 正規化。
|
||
.replace(PATTERN_Roman_numeral_alternative, function(digit) {
|
||
return Roman_numeral_alternative[digit];
|
||
});
|
||
}
|
||
|
||
_.to_Roman_numeral = to_Roman_numeral;
|
||
_.from_Roman_numeral = from_Roman_numeral;
|
||
_.normalize_Roman_numeral = normalize_Roman_numeral;
|
||
|
||
'ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ'.split('').forEach(function(digit, index) {
|
||
Roman_numeral_alternative[digit] = to_Roman_numeral(index + 1);
|
||
});
|
||
'ⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻ'.split('').forEach(function(digit, index) {
|
||
Roman_numeral_alternative[digit] = to_Roman_numeral(index + 1);
|
||
});
|
||
PATTERN_Roman_numeral_alternative = new RegExp('['
|
||
+ Object.keys(Roman_numeral_alternative) + ']', 'g');
|
||
|
||
if (false)
|
||
(function() {
|
||
for (var i = 1; i < 50000; i++)
|
||
if (i !== CeL.from_Roman_numeral(CeL.to_Roman_numeral(i)))
|
||
throw 'Error: ' + i + ' → ' + CeL.to_Roman_numeral(i)
|
||
+ ' → '
|
||
+ CeL.from_Roman_numeral(CeL.to_Roman_numeral(i));
|
||
});
|
||
|
||
// -----------------------------------------------------------------------------------------------------------------
|
||
|
||
return (_// JSDT:_module_
|
||
);
|
||
}
|