The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Lingua::JA::Numbers;

use 5.008001;
use strict;
use warnings;
use utf8;

our $VERSION = sprintf "%d.%02d", q$Revision: 0.5 $ =~ /(\d+)/g;
require Exporter;
our @ISA = qw(Exporter);
our @EXPORT = qw(
	ja2num num2ja num2ja_ordinal ja_to_number number_to_ja number_to_ja_ordinal
);
our %EXPORT_TAGS = ( 'all' => [ @EXPORT, qw(to_string) ] );
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );

use overload 
    q("") => \&stringify,
    q(0+) => \&numify,
    fallback => 1,
    ;

sub new{
    my $class = shift;
    my ($str, $opt) = @_;
    my $val = $str ? ja2num($str, $opt) : '';
    return bless {
        val => $val,
        opt => $opt || { style => 'kanji' },
    }, $class;
}

sub parse{
    my $self = shift;
    my ($str, $opt) = @_;
    $opt ||= $self->{opt};
    $self->{val} = ja2num($str, $opt);
    $self->{opt} = $opt;
    $self    
}

sub opt{
    my $self = shift;
    $self->{opt} = {
      %{ $self->{opt} },
      @_,
    };
    # use Data::Dumper;
    # print Dumper $self;
    return $self;
}

sub numify    { $_[0]->{val} };
*as_number = \&numify;
sub get_string { num2ja($_[0]->{val}, $_[0]->{opt}) };
*stringify = *as_string = \&get_string;
sub ordinal { num2ja_ordinal($_[0]->{val}, $_[0]->{opt}) };

our $Zero = {
	kanji    => '零',
	daiji    => '零',
	romaji   => 'Zero',
	katakana => 'ゼロ',
	hiragana => 'ぜろ',
};
our $Point = {
	kanji    => '点',
	daiji    => '点',
	romaji   => 'Ten',
	katakana => 'テン',
	hiragana => 'てん',
};
our $Sign = {
	kanji     =>  {'+' => q(), '-' => '−'},
	daiji     =>  {'+' => q(), '-' => '−'},
	romaji    =>  {'+' => '+', '-' => '-'},
	katakana  =>  {'+' => 'プラス', '-' => 'マイナス'},
    hiragana  =>  {'+' => 'ぷらす', '-' => 'まいなす'},
};
our $Zero2Nine = {
	kanji    => [qw(〇 一 二 三 四 五 六 七 八 九)],
	daiji    => [qw(零 壱 弐 参 四 伍 六 七 八 九)],
	daiji_h  => [qw(零 壱 弐 参 肆 伍 陸 漆 捌 玖)],
	romaji   => [qw(Zero Ichi Ni San Yon Go Roku Nana Hachi Kyuu)],
	katakana => [qw(ゼロ イチ ニ サン ヨン ゴ ロク ナナ ハチ キュウ)],
	hiragana => [qw(ぜろ いち に さん よん ご ろく なな はち きゅう)],
};
our $Ten2Thou = { 
	kanji     => [q(), qw(十 百 千)],
	daiji     => [q(), qw(拾 佰 阡)],
	romaji    => [q(), qw(Juu Hyaku Sen)],
	katakana  => [q(), qw(ジュウ ヒャク セン)],
	hiragana  => [q(), qw(じゅう ひゃく せん)],
};
our $Hugenums = {
    kanji    => [qw(極 恒河沙 阿僧祇 那由他 不可思議 無量大数)],  # manman-shin
    daiji    => [qw(極 恒河沙 阿僧祇 那由他 不可思議 無量大数)],  # manman-shin
    romaji   => [qw(Goku Kougasha Asougi Nayuta Fukashigi Muryoutaisuu) ],
    katakana => [qw(ゴク コウガシャ アソウギ ナユタ フカシギ ムリョウタイスウ)],
    hiragana => [qw(ごく こうがしゃ あそうぎ なゆた ふかしぎ むりょうたいすう)],
},

our $Suffices = {
	kanji    => [q(), qw(万 億 兆 京 垓 禾予 穣 溝 澗 正 載), 
	             @{ $Hugenums->{kanji} }],
	daiji    => [q(), qw(萬 億 兆 京 垓 禾予 穣 溝 澗 正 載), 
	             @{ $Hugenums->{kanji} }],
	romaji   => [q(), qw(Man Oku Chou Kei Gai Jo Jou Kou Kan Sei Sai Goku),
	             @{ $Hugenums->{romaji} }],
	katakana => [q(), qw(マン オク チョウ ケイ ガイ ジョ ジョウ コウ ジュン セイ サイ),
	             @{ $Hugenums->{katakana} }],
	hiragana => [q(), qw(まん おく ちょう けい がい じょ じょう こう じゅん せい さい),
	             @{ $Hugenums->{hiragana} }],
};

our %RE_Kana_Fix = (
    SanHyaku   => 'Sanbyaku', 'さんひゃく' => 'さんびゃく', 'サンヒャク' => 'サンビャク',
    RokuHyaku  => 'Roppyaku', 'ろくひゃく' => 'ろっぴゃく', 'ロクヒャク' => 'ロッピャク',
    HachiHyaku => 'Happyaku', 'はちひゃく' => 'はっぴゃく', 'ハチヒャク' => 'ハッピャク',
    SanSen     => 'Sanzen',   'さんせん'   => 'さんぜん',   'サンセン'   => 'サンゼン',
    HachiSen   => 'Hassen',   'はちせん'   => 'はっせん',   'ハチセン'   => 'ハッセン',
);
our $RE_Kana_Fix = join("|", keys %RE_Kana_Fix);

sub num2ja{	
    no warnings 'uninitialized';
    # use bignum;
    my ($num, $opt) = @_;
    # warn $num;
    my $style       = $opt->{style} || 'kanji';

    my $zero = $opt->{zero}     ? $opt->{zero}
	         : $Zero->{$style} ;
    return $zero unless $num;
    my ($sig, $int, $fract, $exp) 
	    = ($num =~ /([+-])?(\d+)(?:\.(\d+))?(?:[eE]([+-]?\d+))?/io);
    # warn join ",", ($num, $sig, $int, $fract, $exp);
    my $scientific = sub {
        my $first = substr($int, 0, 1, '');
        $exp += length($int);
        return num2ja("$sig$first.$int$fract" . "e$exp", $opt);
    };
    my $manman = '';
    if (length($int) > 48 and $opt->{manman}) {
        if (length($int) > 96) { # Resort to Scientific Notation
            return $scientific->()
        }
        $int =~ s/(.*)(.{48})\z/$2/o;
        my $huge = $1;
        my @shins;
        push @shins, $1 while $huge =~ s/(\d{8})$//g; # idea from commify hack
        push @shins, $huge if $huge;
        my $suffix = 0;
        for my $shin (@shins) {
            if ($shin eq '00000000') {
                $suffix++;
                next;
            }
            $manman = num2ja($shin, $opt) . $Hugenums->{$style}[$suffix++] . $manman;
        }
    } else {
        if (length($int) > 72) { # Resort to Scientific Notation
            return $scientific->();
        }
    }
    my $sign        = $opt->{sign}     ? $opt->{sign} : $Sign->{$style};
    my $zero2nine   = $opt->{zero2nine}  ? $opt->{zero2nine}
		            : $opt->{daiji} >= 2 ? $Zero2Nine->{daiji_h}
	                : $opt->{daiji} == 1 ? $Zero2Nine->{daiji}
	                : $Zero2Nine->{$style};
    my $ten2thou    = $opt->{ten2thou} ? $opt->{ten2thou}
		            : $opt->{daiji} || $opt->{daiji_h} ? $Ten2Thou->{daiji} 
	                : $Ten2Thou->{$style} ;
    my $suffices = $opt->{suffices} ? $opt->{suffices}
	                : $opt->{daiji} || $opt->{daiji_h} ? $Suffices->{daiji}
	                : $Suffices->{$style} ;
    my ($seisuu, $shousuu, $beki) = ();
    my @shins;
    push @shins, $1 while $int =~ s/(\d{4})$//g; # idea from commify hack
    push @shins, $int if $int;
    my $suffix = 0;
    for my $shin (@shins) {
	if ($shin eq '0000') {
	    $suffix++;
	    next;
	}
	my $sens    = '';
	my $keta    = 0;
	# warn $man;
	for my $digit (reverse split //, $shin) {
	    if ($opt->{fixed4} or $opt->{with_arabic}) {
            $sens = 
                ($opt->{with_arabic} ? $digit : $zero2nine->[$digit])
                . $sens;
	    } else {
            my $suuji = 
                ($digit == 1 
                 and !$opt->{p_one} 
                 and $keta > 0) ? ''
                 :  $zero2nine->[$digit];
            $sens = $suuji . $ten2thou->[$keta] . $sens
                if $digit != 0;
	    }
	    $keta++;
	}
	# $sens or next;
	$seisuu = $sens . $suffices->[$suffix++] . $seisuu;
    }
    my $result =  $sign->{$sig} . $manman . $seisuu;
    $result ||= $zero;
    if ($fract) {
        while ($fract =~ /(\d)/g) {
            $shousuu .= $zero2nine->[$1];
        }
        my $point = $opt->{point} ? $opt->{point}
                  : $Point->{$style};
        $result .=  $point . $shousuu;
    }
    if ($exp) {
        $result .= 
            $opt->{romaji}   ? 'KakeruJuNo'       . num2ja($exp, $opt) .'Jou'
            : $opt->{katakana} ? 'カケルジュウノ' . num2ja($exp, $opt) .'ジョウ'
            : $opt->{hiragana} ? 'かけるじゅうの' . num2ja($exp, $opt) .'じょう'
            :                    '掛ける十の'     . num2ja($exp, $opt) . '乗';
    }
    if ($style =~ /(?:romaji|[k|g]ana)$/){
        $result =~ s/($RE_Kana_Fix)/$RE_Kana_Fix{$1}/ig;
    }
    return $result;
}

*number_to_ja = \&num2ja;

our $Ordinal = {
    kanji    => '番',
    romaji   => 'Ban',
    hiragana => 'ばん',
    katakana => 'バン',
};

sub num2ja_ordinal{
    my ($num, $opt) =  @_;
    my $style    = $opt->{style} || 'kanji';
    my $ordinal = $opt->{ordinal} || $Ordinal->{$style};
    return num2ja(@_) . $ordinal;
}
*number_to_ja_ordinal = \&num2ja_ordinal;


our %RE_Points  = (
		   '.'   => '.',
		   '点'   => '.',
		   'てん' => '.',
		  );
our $RE_Points = join ('|', keys %RE_Points);

our %RE_Zero2Nine = (
    '零' => 0, '〇' => 0, 'ぜろ' => 0, 'れい' => 0, Zero  => 0,
    '一' => 1, '壱' => 1, 'いち' => 1,              Ichi  => 1,
    '二' => 2, '弐' => 2, 'に'   => 2,              Ni    => 2,
    '三' => 3, '参' => 3, 'さん' => 3,              San   => 3,
    '四' => 4, '肆' => 4, 'し'   => 4, 'よん' => 4, Shi   => 4, Yon => 4,
    '五' => 5, '伍' => 5, 'ご'   => 5,              Go    => 5,
    '六' => 6, '陸' => 6, 'ろく' => 6,              Roku  => 6,
    '七' => 7, '漆' => 7, 'なな' => 7, 'しち' => 7, Nana  => 7, Shichi => 7,
    '八' => 8, '捌' => 8, 'はち' => 8,              Hachi => 8,
    '九' => 9, '玖' => 9, 'きゅう' => 9,            Kyuu  => 9, 
);
our $RE_Zero2Nine = join ('|', keys %RE_Zero2Nine);

our %RE_Ten2Thou = ( 
    '十' => 1, '拾' => 1, 'じゅう' => 1,  Juu   => 1,
    '百' => 2, '佰' => 2, 'ひゃく' => 2,  Hyaku => 2,
    '千' => 3, '阡' => 3, 'せん'   => 3,  Sen   => 3,
);
our $RE_Ten2Thou = join ('|', keys %RE_Ten2Thou);

our %RE_Suffices = (
    '万'   => 4, '萬' => 4, 'まん'   => 4,  Man  => 4,
    '億'   => 8,            'おく'   => 8,  Oku  => 8,
    '兆'   => 12,           'ちょう' => 12, Chou => 12,
    '京'   => 16,           'けい'   => 16, Kei =>  16,
    '垓'   => 20,           'がい'   => 20, Gai  => 20,
    '禾予' => 24,           'じょ'   => 24, Jo   => 24,
    '穣'   => 28,           'じょう' => 28, Jou  => 28,
    '溝'   => 32,           'こう'   => 32, Kou  => 32,
    '澗'   => 36,           'かん'   => 36, Kan  => 36,
    '正'   => 40,           'せい'   => 40, Sei  => 40,
    '載'   => 44,           'さい'   => 44, Sai  => 44,
    '極'   => 48,           'ごく'   => 48, Goku => 48,
    '恒河沙' => 52,         'こうがしゃ' => 52, Kougasha     => 52,
    '阿僧祇' => 56,         'あそうぎ'   => 56, Asougi       => 56,
    '那由他' => 60,         'なゆた'     => 60, Nayuta       => 60,
    '不可思議' => 64,       'ふかしぎ'   => 64, Fukashigi    => 64,
    '無量大数' => 68,       'むりょうたいすう' => 68, Muryoutaisuu => 68,    
);
our $RE_Suffices = join ('|', keys %RE_Suffices);

our %RE_Hugenums = (
    '極'       => 48, 'ごく'             => 48, Goku => 48,
    '恒河沙'   => 56, 'こうがしゃ'       => 56, Kougasha     => 56,
    '阿僧祇'   => 64, 'あそうぎ'         => 64, Asougi       => 64,
    '那由他'   => 72, 'なゆた'           => 72, Nayuta       => 72,
    '不可思議' => 80, 'ふかしぎ'         => 80, Fukashigi    => 80,
    '無量大数' => 88, 'むりょうたいすう' => 88, Muryoutaisuu => 88,
);
our $RE_Hugenums = join ('|', keys %RE_Hugenums);

our %RE_Fraction = (
    '割' => 1,  'わり' => 1,
    '分' => 2,  'ぶ'   => 2,
    '厘' => 3,  'りん' => 3,
    '毛' => 4,  'もう' => 4,
    '糸' => 4,  'し'   => 4, '絲' => 4,
    '忽' => 5,  'こつ' => 5,
    '微' => 6,  'び'   => 6,
    '繊' => 7,  'せん' => 7,
    '沙' => 8,  'しゃ' => 8,
    '塵' => 9,  'じん' => 9,
    '埃' => 10, 'あい' => 10,
    '渺' => 11, 'びょう' => 11,
    '漠' => 12, 'ばく' => 12,
    '模糊' => 13, 'もこ' => 13,
    '逡巡' => 14, 'しゅんじゅん' => 14,
    '須臾' => 15, 'しゅゆ' => 15,
    '瞬息' => 16, 'しゅんそく' => 16,
    '弾指' => 17, 'だんし'     => 17,
    '刹那' => 18, 'せつな'     => 18,
    '六徳' => 19, 'りっとく'   => 19,
    '空虚' => 20, 'くうきょ'   => 20, 
    '清浄' => 21,  'せいじょう' => 21, '空' => 21, 'くう' => 21,
    '清'   => 22,  'せい'       => 22,
    '浄'   => 23,  'じょう'     => 23,
    '阿頼耶' => 24, 'あらや'    => 24,
    '阿摩羅' => 25,  'あまら'    => 25,
    '涅槃寂靜' =>26,  'ねはんじゃくじょう' => 26,
);
our $RE_Fraction = join ('|', keys %RE_Fraction);

our %RE_Op = (
    '掛ける' => '*', 'かける' => '*', Kakeru => '*',
    '割る'   => '/', 'わる'   => '/', Waru   => '/',  
    '足す'   => '+', 'たす'   => '+', Tasu   => '+', 'ぷらす'   => '+',
    'と'     => '+', 
    '引く'   => '-', 'ひく'   => '-', Hiku   => '-', 'まいなす' => '-',
);
our $RE_Op = join('|' => map { quotemeta($_) } keys %RE_Op);
our $RE_Numerals =
    qr{(?:\d
         |$RE_Zero2Nine|$RE_Ten2Thou|$RE_Suffices|$RE_Fraction|$RE_Points)+}ixo;
our %RE_Fix_Kana = reverse %RE_Kana_Fix;
our $RE_Fix_Kana = join("|", keys  %RE_Fix_Kana);

sub ja2num{
    no warnings 'uninitialized';
    my ($ja, $opt) = @_;
    # https://twitter.com/Nyaboo/status/575196780993761280
    if ($ja !~ /[+\-\.\deE]/) {
        no warnings 'numeric';
        my $num = $ja + 0;
        return $num if $num;
    }
    $ja or return; # or it croaks under -T @ eval
    $ja =~ s/[\s\x{3000}]//g;
    $ja =~ tr[0-9][0-9];
    $ja =~ tr[ァ-ン][ぁ-ん];
    $ja =~ s/($RE_Fix_Kana)/$RE_Fix_Kana{ucfirst $1}/igx;
    $ja =~ s{ (?:の|ノ|No)($RE_Numerals)(?:乗|じょう|ジョウ|Jou) }
            { "**" . $1 }iegx;
    $ja =~ s{ ($RE_Numerals)  }{ _ja2num($1, $opt) }iegx;	        
    $ja =~ s{ ($RE_Op) }{ $RE_Op{ucfirst $1} }igx;
    $ja =~ tr[()+−×÷][\(\)\+\-\*\/];
    # to be secure;  that way no dangerous ops are passed
    $ja =~ tr/[G-Z]//d; 
    my $result = eval qq{ use bignum; $ja};
    $@ and $opt->{debug} and warn "$ja => $@";
    $opt->{debug} and warn qq{ja2num("$ja") == $result};
    return qq($result);
}
*ja_to_number = \&ja2num;

sub _ja2num{
    no warnings 'uninitialized';
    my ($ja, $opt) = @_;
    $ja or return;
    my $manman = '';
    if ($opt->{manman}){ # wierd hack
        $ja =~ s{ \G(.*?)($RE_Hugenums) }
                { my ($p, $q) = ($1, $2);
                  $p ||= 1;
                  $manman .= _ja2num($p, $opt) . "e" . $RE_Hugenums{$q} . '+';
                  q();
                }iegx;
    }
    $ja =~ s{ ($RE_Zero2Nine) }{$RE_Zero2Nine{ucfirst $1}}igx;
    $ja =~ s{ (\d*)($RE_Ten2Thou)  }
	        { my $n = $1 || 1;
	          $n.'e'.$RE_Ten2Thou{ucfirst $2}.'+' }iegx;
    $ja =~ s{ ([\d\+\-e]+)($RE_Fraction)  }
	        { qq{($1)} . '*1e-' . $RE_Fraction{ucfirst $2} . '+'}iegx;
    $ja =~ s{ \G(.*?)\+?($RE_Suffices) }
            { my $p = $1 || 1;
              "($p)*1e" . $RE_Suffices{ucfirst $2} . '+' 
             }iegx;
    $ja =~ s{ ($RE_Points) }{ '.' }iegx;
    # warn $ja;
    $ja = $manman . $ja;
    $ja =~ s{ \+\s*(\)|\z) }{$1}gx;
    # warn $ja;
    my $result = eval qq{ use bignum; $ja };
    $@ and $opt->{debug} and warn "$ja =>\n $@";
    $opt->{debug} and warn qq{_ja2num("$ja") == $result};
    return qq($result);
}

our %RE_TO_STRING_EXCP = (
    Sanbyaku => 'san-byaku',
    Roppyaku => 'ro-p-pyaku',
    Happyaku => 'ha-p-pyaku',
    Sanzen   => 'san-zen',
    Hassen   => 'ha-s-sen',
);
our $RE_TO_STRING_EXCP = join("|", keys %RE_TO_STRING_EXCP);

sub to_string{
    my ($str,$opt) = @_;
    $opt ||= {};
    $opt->{style} = "romaji";
    delete $opt->{daiji};
    delete $opt->{daiji_h};
    my $ja = __PACKAGE__->new($str, $opt);
    my @words = 
      map { s/($RE_TO_STRING_EXCP)/$RE_TO_STRING_EXCP{$1}/i; lc $_ }
          ($ja->get_string =~ /([A-Z][a-z]*)/g);
    return @words;
}

1;
__END__
# Below is stub documentation for your module. You'd better edit it!

=head1 NAME

Lingua::JA::Numbers - Converts numeric values into their Japanese string equivalents and vice versa

=head1 VERSION

$Revision: 0.5 $ $Date: 2015/03/10 11:04:45 $

=head1 SYNOPSIS

  use Lingua::JA::Numbers;

  # OO Style
  my $ja = Lingua::JA::Numbers->new(1234567890, {style=>'romaji'});
  # JuuNiOkuSanzenYonHyakuGoJuuRokuManNanaSenHappyakuKyuuJuu
  # $ja->get_string is implictly called
  print "$ja\n"; 
  print $ja+0, "\n";
  # 1234567890
  # $ja->number is implicitly called.
  # 1234567890

  # Functional Style
  my $str = ja2num(1234567890, {style=>'romaji'});
  print "$str\n";
  # JuuNiOkuSanzenYonHyakuGoJuuRokuManNanaSenHappyakuKyuuJuu
  print num2ja($str), "\n";
  # 1234567890

=head1 INSTALLATION

To install this module type the following:

   perl Makefile.PL
   make
   make test
   make install

=head1 DEPENDENCIES

This module requires perl 5.8.1 or better.  It also uses L<bignum> internally (that comes with perl core).

=head1 DESCRIPTION

This module converts Japanese text in UTF-8 (or romaji in ascii) to number, AND vice versa.  Though this pod is in English and all examples are in romaji to make L<http://search.cpan.org/> happy, this module does accept Japanese in UTF-8.  Try the code below to see it.

  perl -MLingua::JA::Numbers \
    -e '$y="\x{4e8c}\x{5343}\x{4e94}"; printf "(C) %d Dan Kogai\n", ja2num($y)'

=head2 CAVEAT

DO NOT BE CONFUSED WITH L<Lingua::JA::Number> by Mike Schilli.  This module is far more comprehensive.  As of 0.03, it even does its to_string() upon request.

=head2 METHODS

This module supports the following methods.  They are compliant with L<Lingua::En::Numbers> and others.

=over 2

=item -E<gt>new($str [, {key=>var ...} ])

Constructs an object via C<$str>.  String can either be number or a string in Japanese that represents a number. Optionally take options.  See L</Functions> for options.

=item -E<gt>parse($str, [, {key=>var ...} ])

Parses C<$str>.

=item -E<gt>opt(key => var)

Changes internal options.

=item -E<gt>get_string
=item -E<gt>stringify
=item -E<gt>as_string

Stringifies the object accordingly to the options.  The object auto-stringifies via L<overload> so you don't usally need this.

=item -E<gt>as_number
=item -E<gt>numify

Numifies the object. The object auto-numifies via L<overload> so you don't usally need this UNLESS YOU USE THIS MODULE with L<bignum>.  See L</bignum vs. Lingua::JA::Numbers> below.

=back

=head2 Functions

This module supports the funcitons below;

=over 2

=item num2ja($num, [{key => value ... }]);
=item number_to_ja()

Converts the number to Japanese accordingly to the options. C<number_to_ja()> is just an alias to C<num2ja()>.

  # \x{767e}\x{4e8c}\x{5341}\x{4e09}
  num2ja(123)
  # HyakuNijuuSan
  num2ja(123, {style=>"romaji"})

This function supports the options as follows;

=over 2

=item style =E<gt> (kanji|romaji|hiragana|katakana)

Sets which style (well, script but the word "script" is confusing).
You can choose "kanji" (default), romaji, hiragana and katakana.

=item daiji =E<gt> (0|1|2)

When 1, I<daiji> is used. When 2 or larger, even those that are not represented as daiji will be in daiji.  See 
L<http://ja.wikipedia.org/wiki/%E5%A4%A7%E5%AD%97_%28%E6%95%B0%E5%AD%97%29>
for details.

When this option is set to non-zero, C<style> is ignored (kanji).

=item p_one

Forciblly prefix one even when not needed.

  print num2ja(1110, {style=>"romaji"}), "\n";
  # SenHyakuJuu
  print num2ja(1110, {style=>"romaji", p_one=>1}), "\n";
  # IchiSenIchiHyakuIchiJuu

=item fixed4

Just stack numbers for thousands.

  print num2ja(2005, {style=>"romaji"}), "\n";
  NiSenGo
  print num2ja(2005, {style=>"romaji", fixed4=>1}), "\n";
  NiZeroZeroGo

=item with_arabic

Like C<fixed4> but stack these numbers with arabic.

  print num2ja(20050831, {style=>"romaji"}), "\n";
  # NiSenGoManHappyakuSanJuuIchi
  print num2ja(20050831, {style=>"romaji" with_arabic=>1}), "\n";
  # 2005Man0831

=item manman

Depreciated.  When set to non-zero, it 8-digit (4x2) denomination for
'Goku' (10**48) and above.

  print num2ja(10**60, {style=>"romaji"}), "\n";
  # IchiAsougi
  print num2ja(10**60, {style=>"romaji" manman=>1}), "\n";
  # IchiManKougasha

=back

=item ja2num($str, [{key => value ... }]);
=item ja_to_number()

Converts Japanese number to number.  Unlike C<num2ja()>, its counterpart, it supports only one option, C<manman => (0|1)> which toggles 8-digit denomination.

It is pretty liberal on what it takes.  For instance they all return 20050831.

  ja2num("NisenGoManHappyakuSanjuIchi")
  ja2num("NiZeroZeroGoZeroHachiSanIchi")
  ja2num("2005Man0831")

=back

=head2 ja2num() hacks

ja2num() acts like a calculator -- the easiest way to support scientific notation was just that.  Try

  ja2num("6.0225Kakeru10No23Jou")

=head2 to_string() of Lingua::JA::Number

Though not exported by default, This module comes with to_string()
that is (upper-)compatibile with L<Lingua::JA::Number>.

 my @words = Lingua::JA::Numbers::to_string(1234);
 print join('-', @words), "\n";   
 # "sen-ni-hyaku-san-ju-yon"

=head2 EXPORT

ja2num(), num2ja(), num2ja_ordinal(), ja_to_number(), number_to_ja(), number_to_ja_ordinal()

=head1 BUGS

=over 2

=item bignum vs. Lingua::JA::Numbers

Because of L<overload>, The OO approach does not go well with L<bignum>,
despite the fact this module uses it internally.

  use bignum;
  $j = Lingua::JA::Numbers->new("SanTenIchiYon");
  $b = 1 + $ja          # bang! does not work;
  $b = 1 + $ja->numify; # OK

=item Jo, or 10**24

The chacracter Jo (U+25771) which represents ten to twenty-four does not
have a code point in BMP so it is represented in two letters that 
look like one (U+79be U+x4e88)

=back

=head1 SEE ALSO

L<Lingua::En::Numbers>
L<Lingua::En::Number>
L<http://ja.wikipedia.org/wiki/%E6%BC%A2%E6%95%B0%E5%AD%97>

=head1 AUTHOR

Dan Kogai, E<lt>dankogai@dan.co.jpE<gt>

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2005 by Dan Kogai

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.7 or,
at your option, any later version of Perl 5 you may have available.

=cut