#!/usr/bin/perl

#
# denomfind 
#    小数がいくつか与えられたら、それはどんな(共通する)分母の分数であるかを推定する
#   -- developed by 下野寿之 Toshiyuki Shimono in Tokyo, Japan, 
#   -- first developed probably on 2016-06-20
#   -- one debugging on -% and -c on 2022-10-11 
#   -- heavy implementation added on 2022-10-12, 10-13.  
#

use 5.024 ; use strict ; use warnings ;  # s/のr修飾子は v5.14を要求
use Getopt::Std ; getopts 'a:cD:fg:IL:M:QT:y:%2:5:' , \my%o ; 
use POSIX qw[ ceil floor ] ; 
use Term::ANSIColor qw[ :constants color ] ; $Term::ANSIColor::AUTORESET = 1 ;
use Math::BigInt qw[ bgcd ] ; # -Qの指定時に必要となる。
use List::Util qw [ reduce min max ] ;
use FindBin qw[ $Script ] ; 
my $mpu ; # Math::Prime::Util のモジュールがインストールされているか点検。
CHECK {$mpu = eval 'use Math::Prime::Util qw[ factor factor_exp ]; 1 ' || 0 } # 他にBEGIN INIT UNITCHECK が使える。
& test if exists $o{T} ; 
HELP_MESSAGE () unless @ARGV ;

### テストパターン
sub test () { 
  my $cmd ;
  do { 
    no warnings qw [experimental::smartmatch];
    $cmd = "$0 -D0 -M0 -y-2.. -52 -Q -% 4.76 3.94 3.50 26.37 3.58 2.72" when '1'  ; 
    $cmd = "$0 -g8 -D-6%% -a5e-5 -y, 3.14159265358979323" when  '2'  ;
    $cmd = "$0 -y0,3,4,5 -g290,20 -I -% 2.0 50.7 13.5 21.6 6.8" when  '3'  ;
    # ↓ワクチン1回目の例. 整数の分子が見つかるものが複数ある分母を出力。配付資料に253と書かれているが、その数あたりを出力。
    #   四捨五入を2回行って、最下位を1桁ずつ縮めた場合も想定している。従って、誤差も表示すると、最大のずれは0.05%でなく0.055%。
    $cmd = "$0  -y1.. -g230,50 -D-3% -Q -52 -% 48.2 26.9 23.1 21.6 20.0 12.2 6.7 5.9 2.4" when  'v1'  ;
    # ↓ワクチン2回目の例. 整数の分子が見つからないものが3個以下の分母を出力。配付資料の250という数が正しそうであると分かる。
    $cmd = "$0 -y-3.. -g50 -D0 -Q -% 65.2 45.6 35.6 30.8 29.6 26.4 14.8 7.2 5.6 4.8 4.4" when  'v2'  ;
    # ↓ワクチン3回目の例. 整数の分子が見つかるものが複数ある分母を出力。配付資料に分母97とあるが96も3個混じったとも考えられる。
    $cmd = "$0 -y2.. -D0 -Q -% 81.4 63.3 54.6 51.0 46.9 45.9 28.6 17.3 13.3 11.3 10.3" when  'v3'  ;
    # ↓ バンダイのお年玉の2018年の調査で、小学生、中学生、男子、女子の場合
    $cmd = "$0 -y, -52 -g5 -D-3% -Q -% 39.9 30.4 29.5 18.0 17.2" when  'o1'  ;
    $cmd = "$0 -y, -52 -g5 -D-3% -Q -% 33.7 31.8 23.5 22.7 22.0" when  'o2'  ;
    $cmd = "$0 -y, -52 -g5 -D-3% -Q -% 37.5 36.2 27.3 18.8 18.5" when  'o3'  ;
    $cmd = "$0 -y, -52 -g5 -D-3% -Q -% 39.4 35.7 21.5 20.7 18.4" when  'o4'  ;
    ## ↓ https://www.nexer.co.jp を表示する者が運営するサイトのページから。 
    #  ↓ https://trend-research.jp/3038/ の「どのくらいの頻度で..?」
    $cmd = "$0 -% 0.7 1.8 5.5 7.7 18.4 12.9 21.3 8.5 23.2" when 'n1' ; 
    #  ↓ https://prtimes.jp/main/html/rd/p/000000231.000087626.html 月々いくらまでなら?
    $cmd = "$0 -D-2%,----------- -y-1.. -a0.07% -% 11.2 29.3 28.2 11.6 9.7 10.1" when 'n2' ; 
    $cmd = "$0 -g259,1 -a0.194% -D-3% -% 16.9 23.9 31.0 9.9 2.8 5.6" when 'n3' ; # -a で分子をなんとか探索している。
    # ここからは自分で用意したスライドに関して。
    $cmd = "$0 -y0.. -D5 -Q -g141,20 -% 2.0 13.5 21.6 6.8" when 's18' ;
    $cmd = "$0 -y-1.. -D5 -52 -g20 -% 2.0 50.7 13.5 21.6 6.8" when 's19' ;
    $cmd = "$0 -y-1.. -I  -g20 -% 2.0 50.7 13.5 21.6 6.8" when 's20' ; 
    $cmd = "$0 -D5 -I -Q -g150,-inf -% 2.0 13.5 21.6" when 's21' ; 
    $cmd = "$0 -y1.. -g75,20 -D0,----- -Q -% 81.4 63.3 54.6 51.0 46.9 45.9 28.6 17.3 13.3 11.3 10.3" when 's24' ;
    # 機能追加に合わせてテストパターン
    $cmd = "$0 -M0 -D9 33.3% 50.0% 57.1%" when 'D9' ; # -% 以外に%を直接数値に付けることを可能とした。%から&への誤字に注意。
    $cmd = "$0 -y, -L1 -% 3.2 59.0 17.5 20.3" when 'L1' ; # -L の実装の伴うテスト
  } for $o{T} ; 
  say STDERR BOLD ITALIC YELLOW " >> " , $cmd ; 
  system $cmd ; 
  exit ; # systemで exit するはずだが、違うコードを増やすことも考え、ここに exitと書いた。
}

### テスト以外はここから開始


$o{D} =~ s/,(.*)$// if exists $o{D};
my $Demp = $1 if defined $1 ; # // '-' ; 
$o{D} //= 0 unless grep { m/[DIQ]/ } keys %o ; 
$o{g} //= 12 ; # 取得する分母の個数の最大値 なお、コンマ区切りで、取得分母の開始値(最小値)も指定可能。
$o{a} =~ s/(^.*)%$/"$1"*"0.01"/e if exists $o{a} && $o{a} =~ /%$/ ;  # -a の指定が % で終わる場合の処理
$o{y} //= '1..' ; # 何か候補があれば表示とした。

do { pipe *STDIN , my $WH ; print {$WH} join "\n" , splice @ARGV , 0 } ;
my @nums = & readNums ;  # $q は、-y のオプションのパラメータでevalする際に、裏技的に使うことを意図している。
my $q = @nums ;

# ここは、同じ引数で同じ関数を2回(以上)呼び出すという意味で、不効率で冗長とも言える。後で整理。.. 
my @ddg = map { & decDig ($_ ) } @nums ; # 与えられた割合近似値が、それぞれ、小数点以下何桁であるか? dig digit 
my $ymx = max ( exists $o{y} ? & yparse ( $o{y} ) : $q ) ; # y max # 計算が冗長.. 何度も気になる。ƒexp
my $roa = abs 1/$o{a} if exists $o{a} ; # reciprocal $o{a}

my $count = 0 ; # 探索した分母の個数
my $denom = do { $o{g} =~ s/(.+),// && $1 } || 1 ; #　開始する分母の値  $o{g} を書き換える場合に注意。
& main () ; 
END{
  exit unless $count ; # 1個も分母を探索していないなら、すぐ終了。
  exit if 0 eq ($o{2}//'') ;
  my $sum = 0 ; $sum = "$sum" + "$_" for @nums ; 
  my $bf = color 'bold faint white' ;
  print STDERR $bf . scalar @nums . " ratios are given (sum=" . color('reset bold yellow').($sum) . $bf .")." if @nums > 1 ; 
  my $ud = [ 'down to','from','up to' ] -> [ ( $o{g} <=> 0 ) + 1 ] ; # 'from' はかなり無理矢理である。正常な使用の想定外。
  $denom += $o{g} > 0 ? -1 : $o{g} < 0 ? 1 : 0 ; # 1個戻す。
  say STDERR BOLD FAINT " $count denominators have found $ud $denom. ($Script)" ; 
}
exit 0 ;

sub main ( ) { 
  
  my ( @nA , @nB ,@nC) ; # 区間の 閉じた端A と 開いた端B , Cが1の場合。 もしCが0から開区間で、2なら閉区間。
  do { my($A,$B,$C)= realInt ($_) ; push @nA,$A ; push @nB,$B ; push @nC,$C } for @nums ; 

  do { # 出力の1行目
    my @seq = 1 .. $#nums+1 ;
    my @out = qw[denom fit] ; 
    for ( @seq ) { 
      push @out , CYAN $nums[$_-1] if exists $o{D} ; # "f$_:".$nums[$_-1]
      push @out , "numerators_$_" if $o{I} ; # unless $o{v} eq "0" ; 
    }
    push @out , "dividends" if $o{Q} ;
    push @out, BLUE "likelihood ratio" if ($o{L}//'') ne '0' ; 
    say join "\t" , map { UNDERLINE $_ } @out ;
  } ;
  
  $SIG{INT} = sub { say STDERR FAINT BOLD "\$denom=$denom" ; exit } ;
  while ( $count < abs $o{g} ) {  # continue ブロックを用いていることに注意。
    $o{g} < 0 ? last : next if $denom <= 0 ; # 分母$denomが負の場合はまだ上手く動作。0の場合は区間として[0,0]にならずまずい。
    if ( $o{g} > 0 && $denom > 0 ) { # ある時から何も出力しないのに、無限ループとなる事態の回避
      last if exists $o{a} && $ymx < $q && $denom -1 > $roa ;
      last if $ymx < grep { length ($denom -1 ) - 1 >= $_ } @ddg  ; 
    }

    my $kosu = 0 ; # 該当個数(こすう) 
    my @out = () ; # 出力文字列
    my @nu = () ; # numerators 分子の数の集まり ## 
    push @out , "$denom" ; # コロン(:)を以前付与していた
    for my $i ( 0 .. $#nums ) { 
      my ($mA,$mB,$mC) = ( "$nA[$i]" * "$denom" , "$nB[$i]" * "$denom" , $nC[$i] ) ; # 分子の数値に対応
      my @int = numInts ( $mA , $mB , $mC ) ; #区間に含まれる整数の最小と最大。返値の要素数は1の場合も0の場合もある。
      push @nu , $int[0]..$int[-1] if @int ;
      $kosu ++ if @int ;
      push @out , procD( \@int, $nums[$i] , $mA,$mB,$mC ) if exists $o{D} ; 
      push @out , do{ my $str=&sect($mA,$mB,$mC) ; @int ? GREEN $str : $str } if $o{I} ; 
    }
    sub sect($$$){
      return $_[0] < $_[1] ? "[$_[0] $_[1])" : "($_[1] $_[0]]" if $_[2] == 1 ;
      return $_[2] ?  "[$_[0] $_[1]]" : "($_[0] $_[1])" ; #unless $_[2] & 2 ; 
    } ; 

    next unless yfilter ( $kosu ) ;
    state $den1 = $denom ; #print RED $den1;
    my $t = scalar @nums - $kosu ; 
    $kosu = $t==0 ? BRIGHT_RED BOLD $kosu : $t==1 ? YELLOW BOLD $kosu : $t==2 ? $kosu : FAINT $kosu ; 
    splice @out , 1 , 0 , $kosu ; # 出力配列文字列に、個数表記を挿入。
    push @out , procQ( @nu ) if $o{Q} ; # ( $o{Q} ) {    }
    splice @out , @out, 0 , BLUE sprintf '%0.5g', ($den1 / $denom ) ** ( scalar @nums - ($o{L}//0) ) if ($o{L}//'') ne '0' ;
    ( $o{M} // '' ) eq 0? next : ($out[0] .= '.') if @nu && bgcd ($denom, @nu) > 1 ; # 分母にピリオドを付加。
    say join "\t" , map { $_ // '' } @out ; 
    $count ++ ; 
  } continue { 
    $denom += $o{g} < 0 ? -1 : 1 ; 
  }
}

sub procD ( $$ $$$ ) {
  my $str = join '~', map { dform ( $_ , $_[1] ) } @{$_[0]} ;
  return @{$_[0]} ? CYAN $str : $Demp // FAINT sect($_[2],$_[3],$_[4]) ; # $Demp ; 
}

sub dform ( $$ ) { 
  my $d = 0 + $o{D} =~ s/\D+$//r ; # $o{D}の文字列終わりから数字以外の連続を取り除く; + は'+'という文字を含む場合の対策
  return "$_[0]/$denom" if $o{D} eq 0 ;
  if ( $d >= 0 ) { 
    return "$_[0]/$denom=" . (sprintf "%0.${d}f%%" , $_[0]/$denom * 100 ) =~ s/^0+//r if $o{D} =~ /%$/ ;
    return "$_[0]/$denom=" . (sprintf "%0.${d}f" , $_[0]/$denom ) =~ s/^0+//r ;
  }
  $d = - $d ;
  my $e = $_[1] - $_[0]/$denom ; # error と言えようか?
  my $f = "%+0.${d}f" ; # format
  do { $e *= 100 ; $f = "%+0.${d}f%%"} if $o{D} =~ /\d%$/ ; # ‱
  do { $e *= 10000 ; $f = "%+0.${d}f‱"} if $o{D} =~ /\d%%$/ ; # ‱ permyriad‱
  do { $e *= 1000000 ; $f = "%+0.${d}fppm"} if $o{D} =~ /\d(ppm|%%%)$/ ; # ‱ permyriad‱
  return "$_[0]/$denom" . (sprintf $f , $e ) =~ s/^([+-])0+/$1/r ;
}

sub procQ () { 
  my $str = reduce{$a=~/(\d*)$/;if($b-$1==1){$a=~s/~\d+$//;"$a~$b"}else{"$a,$b"} } @_ ; # 連続する数は~で結ぶ。
  if ( @_ >= 2 ) { 
    my $gcd = bgcd map { $_ - $_[0] } @_ [ 1 .. $#_ ] ; 
    my $rem = $_[0] % $gcd ;
    $str .= ' (' . "${gcd}q" . ( $rem ? "+$rem" : '') . ')' if $gcd > 1 ; 
  } elsif ( @_ == 1 ) {
    if ( $mpu ) { 
      my @fac = factor_exp ( $_[0] ) ; 
      if ( @fac ) { 
        if ( @fac == 1 && $fac[0]->[1] == 1 ) { 
          $str .= ' prime' ;
        } else {
          $str .= ' = '.join' x ', map { $$_[1] > 1 ? "$$_[0]^$$_[1]" : $$_[0] } @fac ; 
        }
      }
    } 
  }
  return $str ;
}

sub yfilter ( $ ) { # メモ : state をいくつか使った読解のしにくいプログラムであること、もっと良い方法がありそうなこと。
  state @y = exists $o{y} ? &yparse ( $o{y} ) : () ;
  state @F = map { my $t = $_ ; ( grep { $y[$_] == $t } 0 .. $#y ) ? 1 : 0 } 0 .. scalar @nums ; 
  return ! exists $o{y} || $F[ $_[0] ] ; 

  sub yparse ( $ ) { 
    # オプション -y で与えられたパラメータ文字列 を
    # まず、コンマ(,)で各セクション切り分ける。
    # そして各セクションは .. がある場合は 整数の並びと見なす。
    # ただし .. の前に数値が無い場合はそこに0、..の後に数値が無い場合は、scalar @num の値があると見なす。
    # .. も無くて、数値も無い場合は、 scalar @num の値が1個だけある見なす。
    # 各数値について、負の数(0より小さな数)が与えられた場合は、その値に scalar @num を加算する。
    # このことで、 -y ..2 で 0から2、 -y 5.. で5以上を表す。-y -2.. により、scalar @num -2 以上の値全体を意味する。
    return $q if $_[0] eq '' ; # あまり使わない方が良いかも。zshで、-y '' なら良いが、-y'' でエラーになるため。
    my @sec = map { [split/\.\./, $_, -1 ] } split /,/, $_[0],-1 ; 
    for ( @sec ) { 
      my @t = map { $_ eq '' ? undef : $_ } map { m/^-/ ? $_+$q : $_ } @{$_} ;
      $_ = @t ==2 ? [ $t[0]//0 , $t[1]//$q ] : [ $t[0]//$q ] ;
    }
    return map { my @t=@{$_} ; $t[0]..$t[-1] } @sec ; 
  }
}

# 半区間 [ $x , $y ) when $x<$y または ( $y , $x ] when $y<$x に、何個の整数が含まれるか。
sub numInts ( $$$ ) { # $A,$B,$Cの方が自然な気もする。とりあえず$w はweightのつもり。「端点の閉の個数」
  my ( $x, $y , $w ) = @_  ; 
  my $n=  $w==1? $x<$y?ceil($y)-ceil($x):floor($x)-floor($y):$w==2?floor($y)-ceil($x)+1 :ceil($y)-floor($x)-1;
  #return () if $n == 0 ;
  my @Z= $w==1?$x<$y?(ceil($x),ceil($y)-1):(floor($y)+1,floor($x)):$w==2?(ceil($x),floor($y)) : (floor($x)+1,ceil($y-1) );
  #@Z = ( $Z[0] ) if $Z[0] == $Z[1] ; 
  return $Z[0]>$Z[1]? () : $Z[0]<$Z[1] ? @Z : $Z[0] ; 
}

sub decDig ( $ ) { # 「小数点以下に数が何桁あるか」を小数点の位置から算出して返す
  my $pos = rindex $_[0] , '.' ; 
  return $pos == -1 ? 0 : length ( $_[0] ) - ( $pos + 1 ) ; 
}

sub realInt ( $ ) { 
  # 返す値は、配列で3番目の要素の値は、0 (開区間), 1(半区間), 2(閉区間)
  # 半区間の場合、区間表記 [A,B) または B<Aなら(B,A] のつもりで、AとBの順に返す。<-- - 
  return $_[0] - $o{a} , $_[0] + $o{a} , 2 if exists $o{a} ; 
  # 返す値は2の配列の要素であり、、意図する半開区間に対して、1番目は閉じた方であり、2番目は開いた方である。
  my $e10 = "0.1" ** decDig $_[0] ; # 10進数文字列を使っている。これで、内部2進数の問題を回避。
  return $_[0] , "$_[0]" - "$e10" , 1 if $o{c} ; # 切り上げの場合
  return $_[0] , "$_[0]" + "$e10" , 1 if $o{f} ; # 切り捨ての場合

  my $e05  = "$e10" * "0.5" ;   # 区間の半分の幅である。
  return "$_[0]" - "$e05"  , "$_[0]" + "$e05" , 1 if ! exists $o{5} ; # 単純な四捨五入の場合
  # ↓「-5に数値」: 既に四捨五入された数の最下位をさらに四捨五入した場合に対応。使う場合は-52が多いであろう。
  return "$_[0]" - "$e10"*('0.'.5x$o{5}) , "$_[0]"+"$e05" , 1 if $o{5} =~ /^\d+$/ ;

  # 最下位の表示が奇数であるか と 奇数(odd)丸めか の xor 演算
  my $flg =  ( 1 & substr $_[0],-1,1 ) ^ ( $o{5} eq 'e' || $o{5} eq 'E' ) ; # [oO]で無ければ[eE]のはず。
  return "$_[0]" - "$e05"  , "$_[0]" + "$e05" , ($flg << 1) ; # $flg を　2倍している。
}

sub readNums ( ) { 
  my @nums = () ; 
  while ( <> ) { chomp ; push @nums , $_ } ; # 以前は既に使わないオプション-iで標準入力から読み取ることもしていた。
  for ( @nums ) {
    $_ = do { my $d = 2 + decDig $_ ; sprintf "%0.${d}f" , "$_" * "0.01" } if $o{'%'} || s/%$// ; # 百分率の場合の処理
    s/^-// for @nums ; # 負の値は、(-1)倍される。
  }
  return @nums ; 
}

sub VERSION_MESSAGE {}
sub HELP_MESSAGE {
  use FindBin qw[ $Script ] ;
  $ARGV[1] //= '' ;
  open my $FH , '<' , $0 ;
  while(<$FH>){
    s/\$0/$Script/g ;
    print $_ if $ARGV[1] eq 'opt' ? m/^\ +\-/ : s/^=head1// .. s/^=cut// ;
  }
  close $FH ;
  exit 0 ;
}


=encoding utf8

=head1

 $0 

   小数がA個与えられたら、それらがどんな共通する分母の、分数であったかの推定を
   するための数値計算プログラム。切り捨てと切り上げも仮定できるが、未指定なら四捨五入を仮定。

 出力の読み方: 
   1列目:  N  Nは共通する分母を表す。(見つかった分子候補全てとNの全てに対して、1以外の約数かあれば、ピリオドを付加。)
   2列目:  B  Nが分母と仮定した場合の、分子が整数になり得る場合の個数を数える。4通りの色で着色(最大,最大-1,最大-2,それ以外)。
   3列目以降: 
     -D の指定で、各列は与えられた数値を近似する割り算の式を表示。候補が複数ある場合は分子が最小と最大の場合を;で区切て出力。
     -I の指定で、分子になり得る数値の範囲を半区間表記で示す。整数を含む区間の場合は、緑色に着色する。
     -Q の指定で、分子となる値を列挙する。-Dや-Iよりも簡便な表示となるが、情報が落ちる場合がある。

 使用例: 
    $0 -g 50 0.25 0.33 
     # 四捨五入して、0.25 と 0.33 になるような分数で同じ分母を持つものを見つける。
     # 出力される各行の最終行が 2 となるものを探せば良い。

 オプション: 

  -c : 入力された数は、切り上げられた数であると見なす。(ceil)
  -f : 入力された数は、切り捨てられた数であると見なす。(floor)
  -g num : 何個の出力を得るかの指定。未指定なら12。"Inf" も指定可能。(get, greatest)
  -g start,num : コロンを使うと、分母をどの数から始めるかを指定が可能。(startからnum個)
  -g start,-num : numにマイナスの符号を付加すると、start から 1ずつ減少させながら 最大 num個探索する。
  -y numranges : fit値(整数を区間内に持つ対応する分子の個数)がどの値の場合に、その分母の行を出力するかを指定する。,や..を使って指定。
  -y , : fit値が最大の場合(引数として与えた割合の近似値の個数)のみ出力。( -y のパラメタは書式はやや複雑。)
  -a num : 与えられた数値との差の絶対値が num 以下である 分数表示を見つける。(実験的実装。)
  -5 2 : 既に四捨五入された値の最下位1桁をさらに四捨五入していたことを仮定する。(例. 0.45 → 0.5 → 1)
  -5 num : 既に四捨五入された値のさらに何度も最下位1桁を四捨五入したことを仮定。計num回四捨五入をしたと仮定する。
  -5 e : 偶数丸めをしたと見なす。(規格の ISO 31-0とJIS Z8401 を参照。) -5 e でなくて -5 E でも良い。
  -5 o : 奇数丸めと見なす。-5 oも可。近似したい数の最下位が5の場合に、それを落としたい場合、最下位が奇数になるようにする。
  
  -% : 入力された数はパーセンテージ表記(百分率)であると見なし、内部的には100分の1倍される。

  -D は下記約7行で説明するように、主に除算の式を出力。除算の式が作れない場合は、分子のあり得る実数範囲を暗く表示(-Iと同様)。
   -D num : 除算の式を出力。numは小数点以下の桁数。四捨五入で除算結果を出力。num=0の場合は、除算結果は見せず式のみ。
   -D num% : 除算の式を出力。%表示。numは%表示で小数点以下の桁数。四捨五入で除算結果を出力。
   -D -num : 除算の式と共に、ずれが分かるように、小数点以下num桁の誤差と共に示す。
   -D -num% : 除算の式と共に、ずれが分かるように、小数点以下num桁のパーセントと共に示す。
   -D -num%% : 除算の式と共に、ずれが分かるように、小数点以下num桁のパーミリアド(1万分の1)と共に示す。
   -D -{N}ppm :  除算の式と共に、ずれが分かるように、小数点以下num桁のppm(100万分の1)と共に示す。
   -D str1,str2 : -D でコンマがある場合は、前半は上記の説明の通りの指定に使われて、後半は該当する分子が存在しない場合のセルを埋めるのに使う。
  -I : 分子の数値を知るべくその半区間を表示す。(Interval)
  -Q : 分子の候補値全て表示。2q+1とか5q+3 など表示(qは何かの整数を意味する)。Math::Prime::Utilが導入済みなら素因数分解もする(割合の数が1個の場合)。
  -2 0 : 二次情報の出力を抑制する。
  -M 0 : ある分母に対して、全ての候補の分子が分母と同じ整数で割れる場合(≒約分可能)は、出力を抑制。

  -L 0 : 尤度比を表示しない
  -L N : 制約(合計が100%など)の個数がN個と仮定して、尤度比を算出する。

  -T testname : このプログラムのテスト。testname は1,2,3, v1,v2,v3, o1,o2,o3,o4, m1 がある。
  --help : このヘルプを表示する。(ただしPod::PerldocJp を一度インストールすると perldoc $0 でも可能。)

  補足 : 
   * 半区間とは、数学的な区間[x,y)または(x,y]のような、それぞれ、x以上y未満、x超y以下のような数全体を表す。
   * 負の数を入力に与えた場合は、その動作によく注意せよ。また 0 や 0.0 を与えた場合も動作に注意。要試行。

  開発メモ: 
     * ただ1個だけの小数点以下8桁の数が渡された場合の良いアルゴリズムを考えたい。
     * -Qの指定による出力については、さらに洗練の余地がある。
     * 最大公約数を計算するために、普段使わない Math::BigInt を用いている。
     * 入力の0に対して、-0.000が発生する謎
     * 同じ数値が与えられたときに different piece で fit も反映させたい。 -s 0 でそれを解除したい。
     * 偶数丸め ( JIS Z 8401 と ISO 31-0 ) に対応したい。-50を割り当てたい。しかし、大幅な改装が必要。
     * 各分母に対して、対数尤度を表示する -Lのオプションを作りたい。

     * "denomfind 90.5%" が意図したとおりにどうさせず。0.905と認識されなかった。90.5と認識された結果が出現し、235,236行目で数値でないという警告が出た。
     * "denomfind -% 90.5%" の動作もおかしい。0.9050 と認識された。
=cut
