BEGIN {
  use POSIX qw(ceil);

  my $mb_Gn = 0;

  foreach (0 .. 3) {
    my $mb_Gx = $mb_Gn++;

    *{'mb_G' . $_} = sub () {$mb_Gx;};
  }

  sub mb_Gn () {$mb_Gn;}
  sub mb_UTF8 () {$mb_Gn + 1;}
  sub mb_MOEINTERNAL () {$mb_Gn + 2;}

  my $mb_Sn = 0;

  my $mb_SL = $mb_Sn++;
  sub mb_SL () {$mb_SL;}

  my $mb_SR = $mb_Sn++;
  sub mb_SR () {$mb_SR;}

  my $mb_SSL = $mb_Sn++;
  sub mb_SSL () {$mb_SSL;}

  my $mb_SSR = $mb_Sn++;
  sub mb_SSR () {$mb_SSR;}

  my $mb_set = 0;

  my $mb_94x94 = $mb_set++;
  sub mb_94x94 () {$mb_94x94;}

  my $mb_96 = $mb_set++;
  sub mb_96 () {$mb_96;}

  my $mb_94 = $mb_set++;
  sub mb_94 () {$mb_94;}

  my $mb_SBC = $mb_set++;
  sub mb_SBC () {$mb_SBC;}

  my $mb_DBC = $mb_set++;
  sub mb_DBC () {$mb_DBC;}

  sub my_nSETs () {$mb_set;}

  require 'mbenclib.ph';
}

sub mb_word_94x94_enc {&MB_WORD_94x94_ENC & ~&MB_NON_UCS_MARK;}
sub mb_word_96_enc {&MB_WORD_96_ENC & ~&MB_NON_UCS_MARK;}
sub mb_word_94_enc {&MB_WORD_94_ENC & ~&MB_NON_UCS_MARK;}
sub mb_word_SBC_enc {&MB_WORD_SBC_ENC & ~&MB_NON_UCS_MARK;}
sub mb_word_DBC_enc {&MB_WORD_DBC_ENC & ~&MB_NON_UCS_MARK;}

sub mb_word_enc {&MB_WORD_ENC & ~&MB_NON_UCS_MARK;}

my $mb_SBC_fc = MB_CTL_FC + 1;
my $mb_DBC_fc = MB_FAKEUCS_FC + 1;

sub matrix_rep {
  my $x = shift;

  if (ref($x) eq 'ARRAY') {
    '[' . join(',', map {&matrix_rep($_)} @$x) . ']';
  }
  elsif (ref($x) eq 'HASH') {
    '+{' . join(',', map {&matrix_rep($_)} %$x) . '}';
  }
  elsif (defined($x)) {
    if ($x eq '' || (!$x && $x ne '0') || $x =~ /\D/) {
      '"' . $x . '"';
    }
    else {
      sprintf('0x%04X', $x);
    }
  }
  else {
    'undef';
  }
}

sub add_to_endecoder_matrix {
  my ($matrix, $suppath, $suptab, $cols, $funs, @rest) = @_;
  my $path = $suppath . sprintf('%02X', $cols->[0]);
  my %subtab = map {/=/ ? ($_ => 0) : ($_ => $suptab->{$_})} keys %$suptab;
  my $submat;

  if (@rest && ref($rest[0]) eq 'ARRAY') {
    my @submat;

    &add_to_endecoder_matrix(\@submat, $path, \%subtab, @{shift(@rest)}) while (@rest);
    $submat = [sort {$a->[0] <=> $b->[0]} @submat];
  }
  else {
    if ($suptab->{$rest[0]} eq '') {
      $suptab->{$rest[0]} = $path;
    }
    else {
      $path = $suptab->{$rest[0]};
    }

    $subtab{$rest[0] . '=' . $path} = 1;
    $submat = $rest[0];
  }

  my $i;

  for ($i = 1 ; $i < @$cols ; $i += 2) {
    my (%tab, $dest, $nchars);

    while (($dest, $nchars) = each %subtab) {
      if ($dest =~ /=/) {
	$tab{$`} = [$suptab->{$dest}, $nchars];
	$suptab->{$dest} += ($cols->[$i] - $cols->[$i - 1] + 1) * $nchars;
      }
      elsif ($suptab->{$dest} eq '') {
	$suptab->{$dest} = $nchars;
      }
    }

    push(@$matrix, [$cols->[$i - 1], $cols->[$i], \%tab, $funs, $submat]);
  }
}

sub conv_matrix_dest_tab2v_loop {
  my ($matrix, $trans, $template) = @_;

  foreach $row (@$matrix) {
    my $tab = $row->[2];

    if (ref($tab) eq 'HASH') {
      my @v = map {$tab->{$_}} @$template;

      $row->[2] = \@v;

      if (ref($row->[4]) eq 'ARRAY') {
	&conv_matrix_dest_tab2v_loop($row->[4], $trans, $template);
      }
      else {
	$row->[4] = $trans->{$row->[4]};
      }
    }
  }
}

sub conv_matrix_dest_tab2v {
  my ($matrix, $tab, $basetab) = @_;
  my %real = map {/=/ ? ($_ => [$basetab->{$`}, $']) : ()} keys %$tab;
  my (@result, @template, %trans, $row);

  foreach (sort {($real{$a}->[0] <=> $real{$b}->[0] ||
		  length($real{$a}->[1]) <=> length($real{$b}->[1]) ||
		  $real{$a}->[1] cmp $real{$b}->[1])} keys %real) {
    my ($dest, $path) = split(/=/, $_, 2);

    if (defined($trans{$dest})) {
      my $old = $result[$trans{$dest}];
      my $max = $real{$_}->[0] + $tab->{$_} - 1;
      my $i = $#$old - 2;

      if ($max > $old->[$i]) {
	push(@$old, $old->[$i] + 1, $max, $tab->{$_}, $path);
      }
    }
    else {
      push(@result, [$real{$_}->[0], $real{$_}->[0] + $tab->{$_} - 1, $tab->{$_}, $path]);
      push(@template, $dest);
      $trans{$dest} = $#template;
    }
  }

  &conv_matrix_dest_tab2v_loop($matrix, \%trans, \@template);
  \@result;
}

sub destv2decmap {
  my ($destv, $func, $arg, $cm) = @_;
  my (@result, $i);

  for ($i = 0 ; $i < @$destv ; ++$i) {
    my $dest = $destv->[$i];
    my $j;

    for ($j = 3 ; $j < @$dest ; $j += 4) {
      push(@result, [@{$dest}[$j-3,$j-2], $func, $arg, $dest->[0], $cm, $i]);
    }
  }

  @result;
}
    
sub same_cols_p {
  my ($a, $b, $i) = @_;

  if (ref($a) eq 'ARRAY') {
    if (ref($b) eq 'ARRAY') {
      for (; $i < @$a && $i < @$b ; ++$i) {
	return '' if (!&same_cols_p($a->[$i], $b->[$i], 0));
      }

      return $i < @$a ? undef : $i < @$b ? undef : 1;
    }
    else {
      return '';
    }
  }
  elsif (ref($b) eq 'ARRAY') {
    return '';
  }
  else {
    return $a eq $b;
  }
}

sub contiguous_rows_p {
  my ($a, $b) = @_;

  ref($a) eq 'ARRAY' && $b eq 'ARRAY' && $a->[1] + 1 == $b->[0] && @$a == @$b && &same_cols_p($a, $b, 2);
}

sub optimize_endecoder_matrix {
  my $matrix = shift;

  if (ref($matrix) eq 'ARRAY' && ref($matrix->[0]) eq 'ARRAY') {
    my $i;

    for ($i = 0 ; $i < @$matrix ; ++$i) {
      &optimize_endecoder_matrix($matrix->[$i]->[4]);
    }

    for ($i = 0 ; $i < $#$matrix ;) {
      if (&contiguous_rows_p($matrix->[$i], $matrix->[$i + 1])) {
	$matrix->[$i]->[1] = $matrix->[$i + 1]->[1];
	splice(@$matrix, $i + 1, 1);
      }
      else {
	++$i;
      }
    }
  }
}

sub make_endecoder_matrix {
  my (@matrix, %tab, $destv);

  &add_to_endecoder_matrix(\@matrix, '', \%tab, @_);
  @matrix = sort {$a->[0] <=> $b->[0]} @matrix;
  $destv = &conv_matrix_dest_tab2v(\@matrix, \%tab, +{'' => 0});
  &optimize_endecoder_matrix($matrix);
  (\@matrix, $destv);
}

sub make_named_encoder {
  my ($set, $name, $basetab) = splice(@_, 0, 3);
  my %basetab = map {$basetab->{$_} =~ /^&MB_.*_ENC\(/ ? ($_ => eval($basetab->{$_})) : ($_ => $basetab->{$_})} keys %$basetab;
  my (@matrix, %tab, $destv);

  &add_to_endecoder_matrix(\@matrix, '', \%tab, @{shift(@_)}) while (@_);
  @matrix = sort {$a->[0] <=> $b->[0]} @matrix;

  if (defined($set)) {
    my $base;

    if (!defined(&{'MB_' . $name . '_FC'})) {
      my $key = '=' . $tab{''};
      my ($fc_min, $fc_max);

      if ($set == mb_SBC) {
	$fc_min = $mb_SBC_fc;
	$mb_SBC_fc += ceil($tab{$key} / MB_SBC_UNIT);
	$fc_max = $mb_SBC_fc - 1;
	$base = MB_WORD_SBC_ENC($fc_min, 0)
      }
      else {
	$fc_min = $mb_DBC_fc;
	$mb_DBC_fc += ceil($tab{$key} / MB_DBC_UNIT);
	$fc_max = $mb_DBC_fc - 1;
	$base = MB_WORD_DBC_ENC($fc_min, 0);
      }

      *{'MB_' . $name . '_FC'} = sub () {$fc_min;};
      *{'MB_' . $name . '_FC_MAX'} = sub () {$fc_max;};
    }

    my $fc = &{'MB_' . $name . '_FC'}();

    $destv = &conv_matrix_dest_tab2v(\@matrix, \%tab, +{%basetab, '' => $base});
  }
  else {
    $destv = &conv_matrix_dest_tab2v(\@matrix, \%tab, \%basetab);
  }

  &optimize_endecoder_matrix(\@matrix);

  *{'mb_' . $name . '_destinations'} = sub () {$destv;};
  *{'mb_' . $name . '_matrix'} = sub () {\@matrix;};

  my $encoder = sub {
    my $rows = \@matrix;
    my @found;

  descend:
    while (@_) {
      my $c = shift(@_);
      my ($b, $e);

      for ($b = 0, $e = @$rows ; $b < $e ;) {
	my $i = int(($b + $e) / 2);
	my $row = $rows->[$i];

	if ($c < $row->[0]) {
	  $e = $i;
	}
	elsif ($c > $row->[1]) {
	  $b = $i + 1;
	}
	elsif (ref($row->[4]) eq 'ARRAY') {
	  push(@found, $c - $row->[0], $row->[2]);
	  $rows = $row->[4];
	  next descend;
	}
	elsif (@_) {
	  last;
	}
	else {
	  $i = $row->[4];

	  my $wc = $destv->[$i]->[0] + $c - $row->[0] + $row->[2]->[$i]->[0];
	  my $j;

	  for ($j = 1 ; $j < @found ; $j += 2) {
	    $wc += $found[$j - 1] * $found[$j]->[$i]->[1] + $found[$j]->[$i]->[0];
	  }

	  return $wc;
	}
      }

      last;
    }

    return undef;
  };

  *{'MB_' . $name . '_ENC'} = $encoder;
  *{'mb_' . $name . '_enc'} = sub {&$encoder & ~MB_NON_UCS_MARK;};
}

my %cm_tab;

sub make_cm_matrix_wcv {
  my ($matrix, $prefix, $i, $nchars, $wcv) = @_;

  $matrix->[$i] = [] if (ref($matrix->[$i]) ne 'ARRAY');

  if (ref($wcv) eq 'ARRAY') {
    push(@{$matrix->[$i]},
	 join("\n",
	      sprintf("mb_wchar_range_t mb_cm_wcv_%s[] = {", $prefix),
	      (map {
		$_->[1] > 0 ? (sprintf("{0x%X,0x%X,%uU},", $_->[0], $_->[0] + $nchars * $_->[1] - 1, $_->[1])) : ("{0,0,0},")
		} @{$wcv}),
	      "};",
	      ''));
  }
}

sub make_cm_matrix_nv {
  my ($matrix, $prefix, $i, $nv) = @_;

  $matrix->[$i] = [] if (ref($matrix->[$i]) ne 'ARRAY');

  if (ref($nv) eq 'ARRAY') {
    push(@{$matrix->[$i]},
	 join("\n",
	      sprintf("mb_char_node_t mb_cm_nodev_%s[] = {", $prefix),
	      @$nv,
	      "};",
	      ''));
  }
}

sub make_cm_matrix_funcdecl {
  my $decl = shift;

  if (ref($decl) eq 'ARRAY') {
    join(',', map {$decl->[$_] eq '' ? 'NULL' : $decl->[$_]} (0, 1));
  }
  else {
    'NULL,NULL';
  }
}

sub make_cm_matrix {
  my ($matrix, $destv, $prefix, $i, $rows) = @_;
  my ($row, $rnc, @nv, $j, $nprefix, $nchars, $tnv, $tcn, $k, @dectab);

  for ($j = 0 ; $j < @$rows ; ++$j) {
    $row = $rows->[$j];
    $rnc = $row->[1] - $row->[0] + 1;
    $nprefix = sprintf('%sx%u', $prefix, $j);

    if (ref($row->[2]) eq 'ARRAY') {
      my $r;

      for ($k = 0 ; $k < @{$row->[2]} ; ++$k) {
	$r = $row->[2]->[$k];
	$dectab[$k] = [] if (ref($dectab[$k]) ne 'ARRAY');
	push(@{$dectab[$k]}, [$r->[0], $r->[0] + $rnc * $r->[1] - 1, $j - $beg]) if ($r->[1] > 0);
      }
    }

    if (ref($row->[4]) eq 'ARRAY') {
      my $old = $cm_tab{$row->[4]};

      &make_cm_matrix_wcv($matrix, $nprefix, $i + 1, $rnc, $row->[2]);

      my $nv_prefix;

      if (ref($old) eq 'ARRAY') {
	($nv_prefix, $tnv, $tnc) = @$old;
      }
      else {
	$nv_prefix = $nprefix;
	($tnv, $tcn) = &make_cm_matrix($matrix, $destv, $nprefix, $i + 1, $row->[4]);
	&make_cm_matrix_nv($matrix, $nprefix, $i + 1, $tnv);
	$cm_tab{$row->[4]} = [$nprefix, $tnv, $tnc, $row->[4]];
      }

      push(@nv, sprintf("{0x%X,0x%X,mb_cm_wcv_%s,%s,{mb_cm_nodev_%s,%uU,mb_cm_dectab_%s,mb_cm_dectab_bv_%s}},",
			@{$row}[0,1], $nprefix, &make_cm_matrix_funcdecl($row->[3]),
			$nv_prefix, $#$tnv + 1, $nv_prefix, $nv_prefix));
      $nchars += $rnc * $tcn;
    }
    else {
      &make_cm_matrix_wcv($matrix, $nprefix, $i + 1, $rnc, $row->[2]);
      push(@nv, sprintf("{0x%X,0x%X,mb_cm_wcv_%s,%s,{NULL,%uU,NULL,NULL}},",
			@{$row}[0,1], $nprefix, &make_cm_matrix_funcdecl($row->[3]), $row->[4]));
      $nchars += $rnc;
    }
  }

  $matrix->[$i] = [] if (ref($matrix->[$i]) ne 'ARRAY');

  my @bv;

  for ($j = $k = 0 ; $k < @dectab ; ++$k) {
    push(@bv, $j);
    $j += ref($dectab[$k]) eq 'ARRAY' ? $#{$dectab[$k]} + 1 : 0;
  }

  push(@{$matrix->[$i]},
       join("\n",
	    sprintf('mb_wchar_range_t mb_cm_dectab_%s[] = {', $prefix),
	    (map {map {sprintf('{0x%X,0x%X,%uU},', @$_)} sort {$a->[0] <=> $b->[0] || $a->[1] <=> $b->[1]} @$_} @dectab),
	    ($j ? () : ('{0x0,0x0,0U},')),
	    '};',
	    sprintf('size_t mb_cm_dectab_bv_%s[] = {', $prefix),
	    (map {sprintf('%uU,', $_)} @bv, $j),
	    '};',
	    ''));

  (\@nv, $nchars);
}

sub make_cm {
  my ($name, $pmatrix, $destv) = @_;
  my (@cmatrix, $nv, $c, $h);

  ($nv) = &make_cm_matrix(\@cmatrix, $destv, $name, 0, $pmatrix);
  &make_cm_matrix_nv(\@cmatrix, $name, 0, $nv);

  $h .= join("\n",
	     sprintf('#define MB_CM_NNODES_%s (%uU)', $name, $#$nv + 1),
	     sprintf('#define MB_WCV_NDESTS_%s (%uU)', $name, $#$destv + 1),
	     sprintf('extern mb_char_map_t mb_cm_%s;', $name),
	     sprintf('extern mb_char_node_t mb_cm_nodev_%s[];', $name),
	     sprintf('extern mb_wchar_range_t mb_cm_dectab_%s[];', $name),
	     sprintf('extern size_t mb_cm_dectab_bv_%s[];', $name),
	     sprintf('extern mb_wchar_range_t mb_wcv_%s[];', $name),
	     '');

  $c .= join('', @{pop(@cmatrix)}) while (@cmatrix);

  $c .= join("\n",
	     sprintf('mb_char_map_t mb_cm_%s = {', $name),
	     sprintf('mb_cm_nodev_%s,', $name),
	     sprintf('%uU,', $#$nv + 1),
	     sprintf('mb_cm_dectab_%s,', $name),
	     sprintf('mb_cm_dectab_bv_%s,', $name),
	     '};',
	     sprintf('mb_wchar_range_t mb_wcv_%s[] = {', $name),
	     (map {sprintf('{0x%X,0x%X,%uU},', @{$_}[0,1,2])} @$destv),
	     '};',
	     '');

  wantarray ? ($c, $h) : $c;
}

sub make_encmap {
  my $name = shift;
  my ($c, $h, $n);
  my @nodev;
  my @iv = ('-1') x 256;

  foreach (@_) {
    my $cm = sprintf($_, $name);
    my $matrix = &{'mb_' . $cm . '_matrix'};
    my $i;

    for ($i = 0 ; $i < @$matrix ; ++$i) {
      my $row = $matrix->[$i];
      my $node = sprintf('{mb_wcv_%s,&mb_cm_nodev_%s[%uU]}', $cm, $cm, $i);

      push(@nodev, $node);
      @iv[$row->[0] .. $row->[1]] = ($#nodev) x ($row->[1] - $row->[0] + 1);
    }
  }

  $c = join("\n",
	    sprintf('mb_encoder_t mb_encmap_nodev_%s[] = {', $name),
	    join(",\n", @nodev),
	    '};',
	    sprintf('short mb_encmap_iv_%s[] = {', $name),
	    join(",\n", map {join(',', @iv[($_ * 16) .. ($_ * 16 + 15)])} (0 .. 15)),
	    '};',
	    sprintf('mb_encoder_map_t mb_encmap_%s = {', $name),
	    sprintf('mb_encmap_nodev_%s,', $name),
	    sprintf('mb_encmap_iv_%s,', $name),
	    '};',
	    '');

  $h = join("\n",
	    sprintf('#define MB_ENCMAP_NNODES_%s (%uU)', $name, $#nodev + 1),
	    sprintf('extern mb_encoder_t mb_encmap_nodev_%s[];', $name),
	    sprintf('extern short mb_encmap_iv_%s[];', $name),
	    sprintf('extern mb_encoder_map_t mb_encmap_%s;', $name),
	    '');

  wantarray ? ($c, $h) : $c;
}

sub make_decmapv {
  my $name = shift;
  my (@decmap, $decmap);

  while (@_) {
    $decmap = shift(@_);

    if (ref($decmap) eq 'ARRAY') {
      push(@decmap, $decmap);
    }
    else {
      $decmap = sprintf($decmap, $name);
      push(@decmap, &destv2decmap(&{'mb_' . $decmap . '_destinations'}(), qw(NULL NULL), '&mb_cm_' . $decmap));
    }
  }

  @decmap = sort {$a->[0] <=> $b->[0] || $a->[1] <=> $b->[1]} @decmap;

  if (@decmap) {
    my ($i, $a, $b);

    $a = $decmap[0];

    for ($i = 1 ; $i < @decmap ; $a = $b) {
      $b = $decmap[$i];

      if ($b->[0] <= $a->[1]) {
	$b->[0] = $a->[1] + 1;

	if ($b->[0] > $b->[1]) {
	  splice(@decmap, $i, 1);
	  next;
	}
      }

      ++$i;
    }
  }

  \@decmap;
}

sub make_decmap {
  my $name = $_[0];
  my $decmap = &make_decmapv;
  my ($utab, $itab);
  my ($c, $h, $i);

  foreach (@$decmap) {
    if ($_->[0] & MB_NON_UCS_MARK) {
      $itab = sprintf('mb_decmap_itab_%s', $name);
    }
    else {
      $utab = sprintf('mb_decmap_utab_%s', $name);
    }
  }

  $c .= sprintf("mb_wchar_range_t mb_decmap_tab_%s[] = {\n", $name);

  for ($i = 0 ; $i < @$decmap ; ++$i) {
    $c .= sprintf("{0x%X,0x%X,%uU},\n", @{$decmap->[$i]}[0,1], $i);
  }

  $c .= join("\n",
	     "};",
	     sprintf('mb_decoder_t mb_decmap_destv_%s[] = {', $name),
	     (map {sprintf('{%s,%s,0x%X,%s,%uU},', @{$_}[2 .. $#$_])} @$decmap),
	     '};',
	     sprintf('mb_decoder_map_t mb_decmap_%s = {', $name),
	     sprintf('mb_decmap_tab_%s,', $name),
	     sprintf('%uU,', $#$decmap + 1),
	     sprintf('mb_decmap_destv_%s,', $name),
	     '};',
	     '');

  $h .= join("\n",
	     sprintf('#define MB_DECMAP_TABSIZE_%s (%uU)', $#$decmap +1),
	     sprintf('extern mb_decoder_map_t mb_decmap_%s;', $name),
	     '');

  wantarray ? ($c, $h) : $c;
}

&make_named_encoder(undef, 'CL', +{'ctl' => 0,
				   'sl01' => 0,
				   'cs_94x94_historical' => 0,
				   'cs_94x94' => 0,
				   'cs_misc' => 0,
				   'cs_return' => 0,
				   'cs_utf8' => 0,
				   'cs_utf16' => 0,
				   'cs_rev' => 0,
				   'cs_94' => 0,
				   'cs_94_i' => 0,
				   'cs_96' => 0,
				   'ssl' => 0,
				   'sl23' => 0,
				   'sr' => 0,
				 },
		    [[0x08,0x0A,0x0C,0x0D],[qw(mb_ctl_encoder)],'ctl'], # BS,TAB,LF,FF,CR
		    [[0x0E,0x0F],[qw(mb_escfun_sl01)],'sl01'],
		    [[0x1B,0x1B],[],
		     [[0x24,0x24],[],
		      [[0x40,0x42],[qw(mb_escfun_cs_94x94)],'cs_94x94_historical'],
		      [[0x28,0x2B],[qw(mb_escfun_cs_94x94)],[[0x40,0x5F],[],'cs_94x94']],
		      ],
		     [[0x25,0x25],[],
		      [[0x21,0x21],[qw(mb_escfun_cs_misc)],[[0x20,0x2F],[],[[0x30,0x3F],[],'cs_misc']]],
		      [[0x40,0x40],[qw(mb_escfun_cs_return)],'cs_return'],
		      [[0x47,0x47],[qw(mb_escfun_cs_utf8)],'cs_utf8'],
		      [[0x2F,0x2F],[qw(mb_escfun_cs_utf16)],[[0x4A,0x4C],[],'cs_utf16']],
		      ],
		     [[0x26,0x26],[qw(mb_escfun_cs_rev)],[[0x40,0x7E],[],'cs_rev']],
		     [[0x28,0x2B],[],
		      [[0x40,0x7E],[qw(mb_escfun_cs_94)],'cs_94'],
		      [[0x21,0x21],[qw(mb_escfun_cs_94_i)],[[0x40,0x7E],[],'cs_94_i']],
		      ],
		     [[0x2D,0x2F],[qw(mb_escfun_cs_96)],[[0x40,0x7E],[],'cs_96']],
		     [[0x4E,0x4F],[qw(mb_iso2022_SSL_encoder)],[[0x20,0x7F],[],'ssl']],
		     [[0x6E,0x6F],[qw(mb_escfun_sl23)],'sl23'],
		     [[0x7C,0x7E],[qw(mb_escfun_sr)],'sr'],
		     ]);

&make_named_encoder(undef, 'GL', +{'' => 0x20},
		    [[0x20, 0x7F],[qw(mb_iso2022_GL_encoder)]]);

&make_named_encoder(undef, '94x94L', +{'' => MB_94x94_LOWER},
		    [[0,(1<<(MB_ESC_IC_LEN+MB_DBC_FC_LEN))-1],[],[[0x21,0x7E],[],[[0x21,0x7E],[]]]]);

&make_named_encoder(undef, '96L', +{'' => MB_96_LOWER},
		    [[0,(1<<(MB_ESC_IC_LEN+MB_ESC_FC_LEN))-1],[],[[0x20,0x7F],[]]]);

&make_named_encoder(undef, '94L', +{'' => MB_94_LOWER},
		    [[0,(1<<(MB_ESC_IC_LEN+MB_ESC_FC_LEN))-1],[],[[0x21,0x7E],[]]]);

&make_named_encoder(undef, 'CLGL', +{'' => 0}, [[0,0x7F],[]]);
&make_named_encoder(undef, 'ASCII', +{'' => 0x21}, [[0x21,0x7F],[]]);

my @decmap_CLGL = &destv2decmap(&mb_CLGL_destinations, qw(mb_CLGL_decoder &mb_G0SL &mb_cm_CLGL));

sub decmap_CLGL () {@decmap_CLGL;}

&make_named_encoder(undef, 'GR', +{'' => 0xA0, 'ssr' => 0},
		    [[0x8E,0x8F],[qw(mb_iso2022_SSR_encoder)],[[0xA0,0xFF],[],'ssr']],
		    [[0xA0,0xFF],[qw(mb_iso2022_GR_encoder)]]);

&make_named_encoder(undef, '94x94R', +{'' => MB_94x94_LOWER},
		    [[0,(1<<(MB_ESC_IC_LEN+MB_DBC_FC_LEN))-1],[],[[0xA1,0xFE],[],[[0xA1,0xFE],[]]]]);

&make_named_encoder(undef, '96R', +{'' => MB_96_LOWER},
		    [[0,(1<<(MB_ESC_IC_LEN+MB_ESC_FC_LEN))-1],[],[[0xA0,0xFF],[]]]);

&make_named_encoder(undef, '94R', +{'' => MB_94_LOWER},
		    [[0,(1<<(MB_ESC_IC_LEN+MB_ESC_FC_LEN))-1],[],[[0xA1,0xFE],[]]]);

sub decmap_94_range {my $fc = shift; (MB_WORD_94_ENC($fc,0),MB_WORD_94_ENC($fc,94-1));}
sub decmap_96_range {my $fc = shift; (MB_WORD_96_ENC($fc,0),MB_WORD_96_ENC($fc,96-1));}
sub decmap_94x94_range {my $fc = shift; (MB_WORD_94x94_ENC($fc,0),MB_WORD_94x94_ENC($fc,94*94-1));}

sub decmap_94 {
  my ($gn, $sn) = @_;
  my $lr = substr($sn, -1);

  ('mb_94' . $lr . '_decoder', '&mb_G' . $gn . 'S' . $sn, MB_94_LOWER, '&mb_cm_94' . $lr, 0);
}

sub decmap_96 {
  my ($gn, $sn) = @_;
  my $lr = substr($sn, -1);

  ('mb_96' . $lr . '_decoder', '&mb_G' . $gn . 'S' . $sn, MB_96_LOWER, '&mb_cm_96' . $lr, 0);
}

sub decmap_94x94 {
  my ($gn, $sn) = @_;
  my $lr = substr($sn, -1);

  ('mb_94x94' . $lr . '_decoder', '&mb_G' . $gn . 'S' . $sn, MB_94x94_LOWER, '&mb_cm_94x94' . $lr, 0);
}

&make_named_encoder(undef, 'UTF8', +{'w11' => 0, 'w16' => 0, 'w21' => 0},
		    [[0xC0,0xDF],[],[[0x80,0xBF],[],'w11']],
		    [[0xE0,0xEF],[],[[0x80,0xBF],[],[[0x80,0xBF],[],'w16']]],
		    [[0xF0,0xF7],[],[[0x80,0xBF],[],[[0x80,0xBF],[],[[0x80,0xBF],[],'w21']]]],
		    );

&make_named_encoder(undef, 'UTF16', +{'' => 0}, [[0x00,0xFF],[qw(mb_utf16_encoder)]]);
&make_named_encoder(undef, 'UTF16BE', +{'' => 0}, [[0x00,0xFF],[qw(mb_utf16_encoder)]]);
&make_named_encoder(undef, 'UTF16LE', +{'' => 0}, [[0x00,0xFF],[qw(mb_utf16le_encoder)]]);

&make_named_encoder(undef, 'MOEINTERNAL', +{'' => 0},
		    [[0xC0,0xFF],[],[[0x80,0xBF],[],[[0x80,0xBF],[],[[0x80,0xBF],[]]]]]);

my ($enclib_c, $enclib_h);

sub {
  while (@_ >= 2) {
    $enclib_c .= $_[0];
    $enclib_h .= $_[1];
    splice(@_, 0, 2);
  }
}->(
    (map {
      &make_cm($_, &{'mb_' . $_ . '_matrix'}(), &{'mb_' . $_ . '_destinations'}());
    } qw(CL GL 94x94L 96L 94L GR 94x94R 96R 94R CLGL ASCII UTF8 UTF16 UTF16BE UTF16LE MOEINTERNAL)),
    &make_encmap('ISO2022', qw(CL GL GR)),
    &make_encmap('UTF8', qw(CL GL UTF8)),
    &make_encmap('UTF16', qw(UTF16)),
    &make_encmap('UTF16BE', qw(UTF16BE)),
    &make_encmap('UTF16LE', qw(UTF16LE)),
    &make_encmap('MOEINTERNAL', qw(CL GL MOEINTERNAL)),
    );

sub get_C_H () {($enclib_c, $enclib_h);}

#print get_C_H;

1;
