version 1.42, 2005/04/21 17:29:16
|
version 1.47, 2005/05/13 21:16:09
|
Line 328 sub initialize_safe_space {
|
Line 328 sub initialize_safe_space {
|
$safeeval->deny(":base_io"); |
$safeeval->deny(":base_io"); |
$safehole->wrap(\&Apache::lonnet::EXT,$safeeval,'&Apache::lonnet::EXT'); |
$safehole->wrap(\&Apache::lonnet::EXT,$safeeval,'&Apache::lonnet::EXT'); |
$safehole->wrap(\&mask,$safeeval,'&mask'); |
$safehole->wrap(\&mask,$safeeval,'&mask'); |
|
$safehole->wrap(\&Apache::lonnet::logthis,$safeeval,'&logthis'); |
$safeeval->share('$@'); |
$safeeval->share('$@'); |
|
# Holds the (computed, final) values for the sheet |
|
# This is only written to by &calc, the spreadsheet computation routine. |
|
# It is read by many functions |
|
$safeeval->share('%sheet_values'); |
my $code=<<'ENDDEFS'; |
my $code=<<'ENDDEFS'; |
# ---------------------------------------------------- Inside of the safe space |
# ---------------------------------------------------- Inside of the safe space |
# |
# |
Line 338 sub initialize_safe_space {
|
Line 343 sub initialize_safe_space {
|
# c: preloaded constants (A-column) |
# c: preloaded constants (A-column) |
# rl: row label |
# rl: row label |
# os: other spreadsheets (for student spreadsheet only) |
# os: other spreadsheets (for student spreadsheet only) |
undef %sheet_values; # Holds the (computed, final) values for the sheet |
undef %t; # Holds the forumlas of the spreadsheet to be computed. Set in |
# This is only written to by &calc, the spreadsheet computation routine. |
# &sett, which does the translation of strings like C5 into the value |
# It is read by many functions |
# in C5. Used in &calc - %t holds the values that are actually eval'd. |
undef %t; # Holds the values of the spreadsheet temporarily. Set in &sett, |
|
# which does the translation of strings like C5 into the value in C5. |
|
# Used in &calc - %t holds the values that are actually eval'd. |
|
undef %f; # Holds the formulas for each cell. This is the users |
undef %f; # Holds the formulas for each cell. This is the users |
# (spreadsheet authors) data for each cell. |
# (spreadsheet authors) data for each cell. |
undef %c; # Holds the constants for a sheet. In the assessment |
undef %c; # Holds the constants for a sheet. In the assessment |
Line 412 returns the number of items in the range
|
Line 414 returns the number of items in the range
|
|
|
#------------------------------------------------------- |
#------------------------------------------------------- |
sub NUM { |
sub NUM { |
my $mask=&mask(@_); |
my $values=&get_values(@_); |
my $num= $#{@{grep(eval("/$mask/"),keys(%sheet_values))}}+1; |
my $num= scalar(@$values); |
return $num; |
return $num; |
} |
} |
|
|
Line 428 sub NUM {
|
Line 430 sub NUM {
|
#------------------------------------------------------- |
#------------------------------------------------------- |
sub BIN { |
sub BIN { |
my ($low,$high,$lower,$upper)=@_; |
my ($low,$high,$lower,$upper)=@_; |
my $mask=&mask($lower,$upper); |
my $values=&get_values($lower,$upper); |
my $num=0; |
my $num=0; |
foreach (grep eval("/$mask/"),keys(%sheet_values)) { |
foreach (@$values) { |
if (($sheet_values{$_}>=$low) && ($sheet_values{$_}<=$high)) { |
if (($_>=$low) && ($_<=$high)) { |
$num++; |
$num++; |
} |
} |
} |
} |
Line 450 returns the sum of items in the range.
|
Line 452 returns the sum of items in the range.
|
|
|
#------------------------------------------------------- |
#------------------------------------------------------- |
sub SUM { |
sub SUM { |
my $mask=&mask(@_); |
my $values=&get_values(@_); |
my $sum=0; |
my $sum=0; |
foreach (grep eval("/$mask/"),keys(%sheet_values)) { |
foreach (@$values) { |
$sum+=$sheet_values{$_}; |
$sum+=$_; |
} |
} |
return $sum; |
return $sum; |
} |
} |
Line 470 compute the average of the items in the
|
Line 472 compute the average of the items in the
|
|
|
#------------------------------------------------------- |
#------------------------------------------------------- |
sub MEAN { |
sub MEAN { |
my $mask=&mask(@_); |
my $values=&get_values(@_); |
my $sum=0; |
my $sum=0; |
my $num=0; |
my $num=0; |
foreach (grep eval("/$mask/"),keys(%sheet_values)) { |
foreach (@$values) { |
$sum+=$sheet_values{$_}; |
$sum+=$_; |
$num++; |
$num++; |
} |
} |
if ($num) { |
if ($num) { |
Line 496 compute the standard deviation of the it
|
Line 498 compute the standard deviation of the it
|
|
|
#------------------------------------------------------- |
#------------------------------------------------------- |
sub STDDEV { |
sub STDDEV { |
my $mask=&mask(@_); |
my $values=&get_values(@_); |
my $sum=0; my $num=0; |
my $sum=0; my $num=0; |
foreach (grep eval("/$mask/"),keys(%sheet_values)) { |
foreach (@$values) { |
$sum+=$sheet_values{$_}; |
$sum+=$_; |
$num++; |
$num++; |
} |
} |
unless ($num>1) { return undef; } |
unless ($num>1) { return undef; } |
my $mean=$sum/$num; |
my $mean=$sum/$num; |
$sum=0; |
$sum=0; |
foreach (grep eval("/$mask/"),keys(%sheet_values)) { |
foreach (@$values) { |
$sum+=($sheet_values{$_}-$mean)**2; |
$sum+=($_-$mean)**2; |
} |
} |
return sqrt($sum/($num-1)); |
return sqrt($sum/($num-1)); |
} |
} |
Line 523 compute the product of the items in the
|
Line 525 compute the product of the items in the
|
|
|
#------------------------------------------------------- |
#------------------------------------------------------- |
sub PROD { |
sub PROD { |
my $mask=&mask(@_); |
my $values=&get_values(@_); |
my $prod=1; |
my $prod=1; |
foreach (grep eval("/$mask/"),keys(%sheet_values)) { |
foreach (@$values) { |
$prod*=$sheet_values{$_}; |
$prod*=$_; |
} |
} |
return $prod; |
return $prod; |
} |
} |
Line 543 compute the maximum of the items in the
|
Line 545 compute the maximum of the items in the
|
|
|
#------------------------------------------------------- |
#------------------------------------------------------- |
sub MAX { |
sub MAX { |
my $mask=&mask(@_); |
my $values=&get_values(@_); |
my $max='-'; |
my $max='-'; |
foreach (grep eval("/$mask/"),keys(%sheet_values)) { |
foreach (@$values) { |
unless ($max) { $max=$sheet_values{$_}; } |
if (($_>$max) || ($max eq '-')) { |
if (($sheet_values{$_}>$max) || ($max eq '-')) { |
$max=$_; |
$max=$sheet_values{$_}; |
|
} |
} |
} |
} |
return $max; |
return $max; |
Line 566 compute the minimum of the items in the
|
Line 567 compute the minimum of the items in the
|
|
|
#------------------------------------------------------- |
#------------------------------------------------------- |
sub MIN { |
sub MIN { |
my $mask=&mask(@_); |
my $values=&get_values(@_); |
my $min='-'; |
my $min='-'; |
foreach (grep eval("/$mask/"),keys(%sheet_values)) { |
foreach (@$values) { |
unless ($max) { $max=$sheet_values{$_}; } |
if (($_<$min) || ($min eq '-')) { |
if (($sheet_values{$_}<$min) || ($min eq '-')) { |
$min=$_; |
$min=$sheet_values{$_}; |
|
} |
} |
} |
} |
return $min; |
return $min; |
Line 591 compute the sum of the largest 'num' ite
|
Line 591 compute the sum of the largest 'num' ite
|
#------------------------------------------------------- |
#------------------------------------------------------- |
sub SUMMAX { |
sub SUMMAX { |
my ($num,$lower,$upper)=@_; |
my ($num,$lower,$upper)=@_; |
my $mask=&mask($lower,$upper); |
my $values=&get_values($lower,$upper); |
my @inside=(); |
my @inside=sort {$a <=> $b} (@$values); |
foreach (grep eval("/$mask/"),keys(%sheet_values)) { |
|
push (@inside,$sheet_values{$_}); |
|
} |
|
@inside=sort(@inside); |
|
my $sum=0; my $i; |
my $sum=0; my $i; |
for ($i=$#inside;(($i>$#inside-$num) && ($i>=0));$i--) { |
for ($i=$#inside;(($i>$#inside-$num) && ($i>=0));$i--) { |
$sum+=$inside[$i]; |
$sum+=$inside[$i]; |
Line 618 compute the sum of the smallest 'num' it
|
Line 614 compute the sum of the smallest 'num' it
|
#------------------------------------------------------- |
#------------------------------------------------------- |
sub SUMMIN { |
sub SUMMIN { |
my ($num,$lower,$upper)=@_; |
my ($num,$lower,$upper)=@_; |
my $mask=&mask($lower,$upper); |
my $values=&get_values($lower,$upper); |
my @inside=(); |
my @inside=sort {$a <=> $b} (@$values); |
foreach (grep eval("/$mask/"),keys(%sheet_values)) { |
|
$inside[$#inside+1]=$sheet_values{$_}; |
|
} |
|
@inside=sort(@inside); |
|
my $sum=0; my $i; |
my $sum=0; my $i; |
for ($i=0;(($i<$num) && ($i<=$#inside));$i++) { |
for ($i=0;(($i<$num) && ($i<=$#inside));$i++) { |
$sum+=$inside[$i]; |
$sum+=$inside[$i]; |
Line 679 sub MAXPARM {
|
Line 671 sub MAXPARM {
|
return $max; |
return $max; |
} |
} |
|
|
|
#------------------------------------------------------- |
|
|
|
=pod |
|
|
|
=item &get_values($lower,$upper) |
|
|
|
Inputs: $lower and $upper, cell names ("X12" or "a150") or globs ("X*"). |
|
|
|
Returns: an array ref of the values of the cells that exist in the |
|
speced range |
|
|
|
=cut |
|
|
|
#------------------------------------------------------- |
|
sub get_values { |
|
my ($lower,$upper)=@_; |
|
$upper = $lower if (! defined($upper)); |
|
my @values; |
|
my ($la,$ld) = ($lower=~/([A-z]|\*)(\d+|\*)/); |
|
my ($ua,$ud) = ($upper=~/([A-z]|\*)(\d+|\*)/); |
|
my ($alpha,$num); |
|
if ($ld ne '*' && $ud ne '*') { |
|
my @alpha; |
|
if (($la eq '*') || ($ua eq '*')) { |
|
@alpha=('A'..'z'); |
|
} else { |
|
if ($la gt $ua) { ($la,$ua)=($ua,$la); } |
|
if ((lc($la) ne $la) && (lc($ua) eq $ua)) { |
|
@alpha=($la..'Z','a'..$ua); |
|
} else { |
|
@alpha=($la..$ua); |
|
} |
|
} |
|
my @num=($ld..$ud); |
|
foreach my $a (@alpha) { |
|
foreach my $n (@num) { |
|
if (exists($sheet_values{$a.$n})) { |
|
push(@values,$sheet_values{$a.$n}); |
|
} |
|
} |
|
} |
|
return \@values; |
|
} else { |
|
$num = '(\d+)'; |
|
} |
|
if (($la eq '*') || ($ua eq '*')) { |
|
$alpha='[A-z]'; |
|
} else { |
|
if ($la gt $ua) { ($la,$ua)=($ua,$la); } |
|
$alpha=qq/[$la-$ua]/; |
|
} |
|
my $expression = '^'.$alpha.$num.'$'; |
|
foreach (grep /$expression/,keys(%sheet_values)) { |
|
push(@values,$sheet_values{$_}); |
|
} |
|
return \@values; |
|
} |
|
|
sub calc { |
sub calc { |
%sheet_values = %t; |
|
my $notfinished = 1; |
my $notfinished = 1; |
my $lastcalc = ''; |
my $lastcalc = ''; |
my $depth = 0; |
my $depth = 0; |
Line 742 ENDDEFS
|
Line 790 ENDDEFS
|
|
|
###################################################### |
###################################################### |
|
|
|
|
###################################################### |
|
|
|
=pod |
|
|
|
=item &mask($lower,$upper) |
|
|
|
Inputs: $lower and $upper, cell names ("X12" or "a150") or globs ("X*"). |
|
|
|
Returns: Regular expression matching spreadsheet cells that are within |
|
the rectangle defined by $lower and $upper. Due to the nature of the |
|
regular expression this result must be used inside an eval(). |
|
|
|
=cut |
|
|
|
###################################################### |
|
{ |
|
|
|
my %memoizer; |
|
|
|
sub mask { |
|
my ($lower,$upper)=@_; |
|
my $key = $lower.'_'.$upper; |
|
if (exists($memoizer{$key})) { |
|
return $memoizer{$key}; |
|
} |
|
$upper = $lower if (! defined($upper)); |
|
# |
|
my ($la,$ld) = ($lower=~/([A-z]|\*)(\d+|\*)/); |
|
my ($ua,$ud) = ($upper=~/([A-z]|\*)(\d+|\*)/); |
|
# |
|
my $alpha=''; |
|
my $num=''; |
|
# |
|
# Do not put parenthases around $alpha. |
|
# $num depends on the value in $1. |
|
if (($la eq '*') || ($ua eq '*')) { |
|
$alpha='[A-z]'; |
|
} else { |
|
if ($la gt $ua) { |
|
my $tmp = $ua; |
|
$ua = $la; |
|
$la = $ua; |
|
} |
|
$alpha=qq/[$la-$ua]/; |
|
} |
|
if ($ld ne '*' && $ud ne '*') { |
|
# Make sure $ld <= $ud |
|
if ($ld > $ud) { |
|
my $tmp = $ud; |
|
$ud = $ld; |
|
$ld = $tmp; |
|
} |
|
# Here we make a regular expression using some advanced regexp |
|
# abilities. |
|
# (\d+) will match the digits of the cell name and dump them in |
|
# to $1 |
|
# (?(?{ ... code ...} pattern_if_true | pattern_if_false)) will |
|
# choose pattern_if_true if { ... code ... } is true and |
|
# pattern_if_false if { ... code ... } is false. |
|
# In this case, pattern_if_true is empty. pattern_if_false is |
|
# 'donotmatch' and will not match our cells because none of |
|
# them end with donotmatch. |
|
# Unfortunately, the use of this type of regular expression |
|
# requires that each match be wrapped in an eval(). Search for |
|
# $mask in this module for examples |
|
$num = '(\d+)(?(?{$1>= '.$ld.' && $1<='.$ud.'})|donotmatch)'; |
|
} else { |
|
$num = '(\d+)'; |
|
} |
|
my $expression = '^'.$alpha.$num.'$'; |
|
$memoizer{$key} = $expression; |
|
return $expression; |
|
} |
|
|
|
# |
|
# Debugging routine |
|
sub dump_memoized_values { |
|
while (my ($key,$value) = each(%memoizer)) { |
|
&Apache::lonnet::logthis('memoizer: '.$key.' = '.$value); |
|
} |
|
return; |
|
} |
|
|
|
} |
|
|
|
## |
## |
## sub add_hash_to_safe {} # spreadsheet, would like to destroy |
## sub add_hash_to_safe {} # spreadsheet, would like to destroy |
## |
## |
Line 907 sub expandnamed {
|
Line 869 sub expandnamed {
|
sub sett { |
sub sett { |
my $self = shift; |
my $self = shift; |
my %t=(); |
my %t=(); |
|
undef(%Apache::Spreadsheet::sheet_values); |
# |
# |
# Deal with the template row |
# Deal with the template row |
foreach my $col ($self->template_cells()) { |
foreach my $col ($self->template_cells()) { |
Line 939 sub sett {
|
Line 902 sub sett {
|
} elsif ( $col =~ /^[A-Z]$/ ) { |
} elsif ( $col =~ /^[A-Z]$/ ) { |
if ($formula !~ /^\!/ && exists($self->{'constants'}->{$cell}) |
if ($formula !~ /^\!/ && exists($self->{'constants'}->{$cell}) |
&& $self->{'constants'}->{$cell} ne '') { |
&& $self->{'constants'}->{$cell} ne '') { |
my $data = $self->{'constants'}->{$cell}; |
$Apache::Spreadsheet::sheet_values{$cell}= |
$t{$cell} = $data; |
eval($self->{'constants'}->{$cell}); |
} |
} |
} else { # $row > 1 and $col =~ /[a-z] |
} else { # $row > 1 and $col =~ /[a-z] |
$t{$cell}=$formula; |
$t{$cell}=$formula; |
Line 959 sub sett {
|
Line 922 sub sett {
|
sub sync_safe_space { |
sub sync_safe_space { |
my $self = shift; |
my $self = shift; |
# Inside the safe space 'formulas' has a diabolical alter-ego named 'f'. |
# Inside the safe space 'formulas' has a diabolical alter-ego named 'f'. |
%{$self->{'safe'}->varglob('f')}=%{$self->{'formulas'}}; |
#%{$self->{'safe'}->varglob('f')}=%{$self->{'formulas'}}; |
# 'constants' leads a peaceful hidden life of 'c'. |
# 'constants' leads a peaceful hidden life of 'c'. |
%{$self->{'safe'}->varglob('c')}=%{$self->{'constants'}}; |
%{$self->{'safe'}->varglob('c')}=%{$self->{'constants'}}; |
# 'othersheets' hides as 'os', a disguise few can penetrate. |
# 'othersheets' hides as 'os', a disguise few can penetrate. |
@{$self->{'safe'}->varglob('os')}=@{$self->{'othersheets'}}; |
#@{$self->{'safe'}->varglob('os')}=@{$self->{'othersheets'}}; |
} |
} |
|
|
## |
## |