1 # Id: SparseMap.pm,v 1.1 2003/06/04 00:27:53 marka Exp
3 # Copyright (c) 2001 Japan Network Information Center. All rights reserved.
5 # By using this file, you agree to the terms and conditions set forth bellow.
7 # LICENSE TERMS AND CONDITIONS
9 # The following License Terms and Conditions apply, unless a different
10 # license is obtained from Japan Network Information Center ("JPNIC"),
11 # a Japanese association, Kokusai-Kougyou-Kanda Bldg 6F, 2-3-4 Uchi-Kanda,
12 # Chiyoda-ku, Tokyo 101-0047, Japan.
14 # 1. Use, Modification and Redistribution (including distribution of any
15 # modified or derived work) in source and/or binary forms is permitted
16 # under this License Terms and Conditions.
18 # 2. Redistribution of source code must retain the copyright notices as they
19 # appear in each source code file, this License Terms and Conditions.
21 # 3. Redistribution in binary form must reproduce the Copyright Notice,
22 # this License Terms and Conditions, in the documentation and/or other
23 # materials provided with the distribution. For the purposes of binary
24 # distribution the "Copyright Notice" refers to the following language:
25 # "Copyright (c) 2000-2002 Japan Network Information Center. All rights reserved."
27 # 4. The name of JPNIC may not be used to endorse or promote products
28 # derived from this Software without specific prior written approval of
31 # 5. Disclaimer/Limitation of Liability: THIS SOFTWARE IS PROVIDED BY JPNIC
32 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
33 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
34 # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JPNIC BE LIABLE
35 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
36 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
37 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
38 # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
39 # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
40 # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41 # ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
53 # BITS => [8, 7, 6], # 3-level map, 2nd level bits=7, 3rd = 6.
54 # MAX => 0x110000 # actually, max + 1.
58 croak
"BITS unspecified" unless exists $self->{BITS
};
59 croak
"BITS is not an array reference"
60 unless ref($self->{BITS
}) eq 'ARRAY';
61 croak
"MAX unspecified" unless exists $self->{MAX
};
63 $self->{MAXLV
} = @
{$self->{BITS
}} - 1;
66 my $lv0size = (indices
($self, $self->{MAX
} - 1))[0] + 1;
68 my @map = (undef) x
$lv0size;
75 my ($self, $n, $val) = @_;
77 croak
"Already fixed" if $self->{FIXED
};
78 carp
("data ($n) out of range"), return if $n >= $self->{MAX
};
80 my @index = $self->indices($n);
82 my $maxlv = $self->{MAXLV
};
86 for ($lv = 0; $lv < $maxlv - 1; $lv++) {
88 $r->[$idx] = $self->create_imap($lv + 1, undef)
89 unless defined $r->[$idx];
93 $r->[$idx] = $self->create_dmap() unless defined $r->[$idx];
94 $self->add_to_dmap($r->[$idx], $index[$maxlv], $val);
99 my $map = $self->{MAP
};
100 my $maxlv = $self->{MAXLV
};
104 carp
"Already fixed" if $self->{FIXED
};
105 $self->collapse_tree();
106 $self->fill_default();
113 my @bits = @
{$self->{BITS
}};
116 print "indices($v,", join(',', @bits), ") = " if $debug;
117 for (my $i = @bits - 1; $i >= 0; $i--) {
119 unshift @idx, $v & ((1 << $bit) - 1);
122 print "(", join(',', @idx), ")\n" if $debug;
129 my $map = $self->{MAP
};
130 my @index = $self->indices($v);
132 croak
"Not yet fixed" unless $self->{FIXED
};
134 my $lastidx = pop @index;
135 foreach my $idx (@index) {
136 return $map->{DEFAULT
} unless defined $map->[$idx];
145 croak
"Not yet fixed" unless $self->{FIXED
};
147 my @maps = $self->collect_maps();
148 my $maxlv = $self->{MAXLV
};
149 my @bits = @
{$self->{BITS
}};
152 for (my $lv = 0; $lv < $maxlv; $lv++) {
155 my $mapsz = @
{$maps[$lv]->[0]};
156 if ($lv < $maxlv - 1) {
158 $offset = @indirect + @
{$maps[$lv]} * @
{$maps[$lv]->[0]};
159 $chunksz = (1 << $bits[$lv + 1]);
165 my $nextmaps = $maps[$lv + 1];
166 foreach my $mapref (@
{$maps[$lv]}) {
167 croak
"mapsize inconsistent ", scalar(@
$mapref),
168 " should be ", $mapsz, " (lv $lv)\n" if @
$mapref != $mapsz;
169 foreach my $m (@
$mapref) {
171 for ($idx = 0; $idx < @
$nextmaps; $idx++) {
172 last if $nextmaps->[$idx] == $m;
174 croak
"internal error: map corrupted" if $idx >= @
$nextmaps;
175 push @indirect, $offset + $chunksz * $idx;
185 my $name = $opt{NAME
} || 'map';
186 my @indirect = $self->indirectmap();
189 my ($idtype, $idcol, $idwid);
192 $max < $_ and $max = $_ foreach @indirect;
198 } elsif ($max < 65536) {
207 $prog = "static const unsigned $idtype ${name}_imap[] = {\n";
209 foreach my $v (@indirect) {
210 if ($i % $idcol == 0) {
211 $prog .= "\n" if $i != 0;
214 $prog .= sprintf "%${idwid}d, ", $v;
223 $self->cprog_imap(@_) . "\n" . $self->cprog_dmap(@_);
228 my @maps = $self->collect_maps();
229 my $elsize = $self->{ELSIZE
};
234 for ($i = 0; $i < $self->{MAXLV
}; $i++) {
235 my $nmaps = @
{$maps[$i]};
236 my $mapsz = @
{$maps[$i]->[0]};
237 push @lines, "level $i: $nmaps maps (size $mapsz) ";
238 push @lines, "[", $nmaps * $mapsz * $elsize, "]" if $elsize;
241 my $ndmaps = @
{$maps[$i]};
242 push @lines, "level $i: $ndmaps dmaps";
243 my $r = $maps[$i]->[0];
244 if (ref($r) eq 'ARRAY') {
245 push @lines, " (size ", scalar(@
$r), ")";
255 $self->_collapse_tree_rec($self->{MAP
}, 0, \
@tmp);
258 sub _collapse_tree_rec
{
259 my ($self, $r, $lv, $refs) = @_;
260 my $ref = $refs->[$lv];
261 my $maxlv = $self->{MAXLV
};
264 return $r unless defined $r;
266 $ref = $refs->[$lv] = [] unless defined $ref;
269 $found = $self->find_dmap($ref, $r);
271 for (my $i = 0; $i < @
$r; $i++) {
272 $r->[$i] = $self->_collapse_tree_rec($r->[$i], $lv + 1, $refs);
274 $found = $self->find_imap($ref, $r);
285 my $maxlv = $self->{MAXLV
};
286 my $bits = $self->{BITS
};
289 $zeros[$maxlv] = $self->create_dmap();
290 for (my $lv = $maxlv - 1; $lv >= 0; $lv--) {
291 my $r = $zeros[$lv + 1];
292 $zeros[$lv] = $self->create_imap($lv, $r);
294 _fill_default_rec
($self->{MAP
}, 0, $maxlv, \
@zeros);
297 sub _fill_default_rec
{
298 my ($r, $lv, $maxlv, $zeros) = @_;
300 return if $lv == $maxlv;
301 for (my $i = 0; $i < @
$r; $i++) {
302 if (defined($r->[$i])) {
303 _fill_default_rec
($r->[$i], $lv + 1, $maxlv, $zeros);
305 $r->[$i] = $zeros->[$lv + 1];
311 my ($self, $lv, $v) = @_;
313 @map = ($v) x
(1 << $self->{BITS
}->[$lv]);
318 my ($self, $maps, $map) = @_;
321 foreach my $el (@
$maps) {
322 next unless @
$el == @
$map;
323 for ($i = 0; $i < @
$el; $i++) {
324 last unless ($el->[$i] || 0) == ($map->[$i] || 0);
326 return $el if $i >= @
$el;
334 _collect_maps_rec
($self->{MAP
}, 0, $self->{MAXLV
}, \
@maps);
338 sub _collect_maps_rec
{
339 my ($r, $lv, $maxlv, $maps) = @_;
340 my $mapref = $maps->[$lv];
342 return unless defined $r;
343 foreach my $ref (@
{$mapref}) {
344 return if $ref == $r;
346 push @
{$maps->[$lv]}, $r;
348 _collect_maps_rec
($_, $lv + 1, $maxlv, $maps) foreach @
{$r};
352 sub add
{confess
"Subclass responsibility";}
353 sub create_dmap
{confess
"Subclass responsibility";}
354 sub add_to_dmap
{confess
"Subclass responsibility";}
355 sub find_dmap
{confess
"Subclass responsibility";}
356 sub cprog_dmap
{confess
"Subclass responsibility";}
360 package SparseMap
::Bit
;
367 @ISA = qw(SparseMap);
371 my $self = $class->SUPER::new
(@_);
372 $self->{DEFAULT
} = 0;
379 $self->add1($_, undef) foreach @_;
384 my $bmbits = $self->{BITS
}->[-1];
386 my $s = "\0" x
(1 << ($bmbits - 3));
391 my ($self, $map, $idx, $val) = @_;
392 vec($$map, $idx, 1) = 1;
396 my ($self, $ref, $r) = @_;
397 foreach my $map (@
$ref) {
398 return $map if $$map eq $$r;
406 my $name = $opt{NAME
} || 'map';
407 my @maps = $self->collect_maps();
408 my @bitmap = @
{$maps[-1]};
410 my $bmsize = 1 << ($self->{BITS
}->[-1] - 3);
413 static const struct {
414 unsigned char bm[$bmsize];
415 } ${name}_bitmap[] = {
418 foreach my $bm (@bitmap) {
421 foreach my $v (unpack 'C*', $$bm) {
423 $prog .= "\n" if $i != 0;
426 $prog .= sprintf "%3d,", $v;
429 $prog .= "\n\t}},\n";
437 package SparseMap
::Int
;
444 @ISA = qw(SparseMap);
448 my $self = $class->SUPER::new
(@_);
449 $self->{DEFAULT
} = 0 unless exists $self->{DEFAULT
};
458 $self->add1($n, $val);
464 my $tblbits = $self->{BITS
}->[-1];
465 my $default = $self->{DEFAULT
};
467 my @tbl = ($default) x
(1 << $tblbits);
472 my ($self, $map, $idx, $val) = @_;
477 my ($self, $ref, $r) = @_;
478 foreach my $map (@
$ref) {
481 for ($i = 0; $i < @
$map; $i++) {
482 last if $map->[$i] != $r->[$i];
484 return $map if $i == @
$map;
493 my $name = $opt{NAME
} || 'map';
494 my @maps = $self->collect_maps();
495 my @table = @
{$maps[-1]};
498 my ($idtype, $idcol, $idwid);
499 my $tblsize = 1 << $self->{BITS
}->[-1];
502 foreach my $a (@table) {
503 foreach my $v (@
$a) {
504 $min = $v if !defined($min) or $min > $v;
505 $max = $v if !defined($max) or $max < $v;
508 if (exists $opt{MAPTYPE
}) {
509 $idtype = $opt{MAPTYPE
};
511 my $u = $min < 0 ?
'' : 'unsigned ';
512 my $absmax = abs($max);
513 $absmax = abs($min) if abs($min) > $absmax;
516 $idtype = "${u}char";
517 } elsif ($absmax < 65536) {
518 $idtype = "${u}short";
520 $idtype = "${u}long";
524 $idwid = decimalwidth
($max);
525 $idwid = decimalwidth
($min) if decimalwidth
($min) > $idwid;
528 static const struct {
529 $idtype tbl[$tblsize];
530 } ${name}_table[] = {
533 foreach my $a (@table) {
537 foreach my $v (@
$a) {
538 my $s = sprintf "%${idwid}d, ", $v;
546 $prog .= "\n\t}},\n";
563 } elsif ($n < 10000) {
565 } elsif ($n < 1000000) {
567 } elsif ($n < 100000000) {