#!/usr/bin/perl 1; ################################################################## ### arithmetic model (table or equation or trivial) evaluation ### ################################################################## sub ALF_EvaluateArithmeticModel { local($model,%arg) = @_; print("# ALF Math Info:\tevaluating arithmetic model $model\t",&ALF_ObjectInfoText($model),"\n") if ($ALF_Debug > 1); local $header = &ALF_FindMatchingObject($model,'HEADER'); local $equation = &ALF_FindMatchingObject($model,'EQUATION'); local $table = &ALF_FindMatchingObject($model,'TABLE'); local $value = $ALF_Value{$model}; local $result; ### trivial arithmetic model if (defined($value)) { $result = $value; ### equation or table } elsif ($header) { local %val = (); local $valid = 1; local @keys = keys %arg; foreach $key (@keys) { local $namematch = &ALF_FindMatchingObject($header,undef,$key); local $typematch = &ALF_FindMatchingObject($header,$key,0); local $match = $namematch || $typematch; if ($match) { local $val = $arg{$key}; print("# ALF Math Info:\tidentified argument $match with value $val for model $model\t",&ALF_ObjectInfoText($match),"\n") if ($ALF_Debug > 1); $val{&UpperCase($key)} = $val if $equation; $val{$match} = $val if $table; } else { $valid = 0; } } local @val = keys %val; local $debug = $ALF_Debug; $ALF_Debug = 2 if &StringMatch($model,@ALF_Probe); if ($equation) { $result = &ALF_EvaluateArithmeticExpression($ALF_Value{$equation},%val); } elsif ($table) { $result = &ALF_EvaluateTable($ALF_Value{$table},0,$#val,%val); } else { print("# ALF Math Error:\tarithmetic model $model(",join(' ',%arg),") misses TABLE or EQUATION\n"); } $ALF_Debug = $debug; } else { print("# ALF Math Error:\tarithmetic model $model(",join(' ',%arg),") misses annotation_value or HEADER\n"); } print("# ALF Math Info:\tevaluation result for arithmetic model $model(",join(' ',%arg),") = $result\n") if ($ALF_Debug > 1); $result; } ######################################## ### arithmetic expression processing ### ######################################## sub ALF_EvaluateArithmeticExpression { local($expression,%arg) = @_; local @result = (); foreach $item (&Array($expression)) { $result[$#result+1] = &ALF_ReplaceItem($item,%arg); } local $result = eval(join(' ',@result)); print("# ALF Math Info:\t$result = ",join(' ',@result),"\n") if ($ALF_Debug > 1); $result; } ############################### ### lookup table processing ### ############################### sub ALF_FindMatchingIndex { local ($match,*table) = @_; local $found; foreach $index (0..$#table) { $found = $index if &StringMatch($table[$index],$match); last if defined($found); } $found; } sub ALF_FindFloorIndex { local ($match,*table) = @_; local $found; foreach $index (reverse 0..$#table) { $found = $index unless ($table[$index] > $match); last if defined($found); } defined($found)? $found : 0; } sub ALF_FindCeilingIndex { local ($match,*table) = @_; local $found; foreach $index (0..$#table) { $found = $index unless ($table[$index] < $match); last if defined($found); } defined($found)? $found : $#table; } sub ALF_FindInterpolationIndices { local ($match,*table) = @_; local $lower = &ALF_FindFloorIndex($match,*table); local $upper = &ALF_FindCeilingIndex($match,*table); if ($lower == $upper) { if ($upper > 0) { $lower = $lower - 1; } else { $upper = 1; } } ($lower,$upper); } sub ALF_EvaluateTable { local($table,$tableoffset,$count,%arg) = @_; local @key = sort(keys %arg); # local @key = keys %arg; if ($ALF_Debug > 1) { print("# ALF Math Info:\tstarted evaluation of ",$count+1,"D table $table\ttableoffset[$table]=$tableoffset\t%x=(",join(' ',%arg),")\n") if ($count==$#key); print("# ALF Math Info:\t\ttableoffset[$table]=$tableoffset\t%x=(",join(' ',%arg),")\n") unless ($count==$#key); } local $key = $key[$count]; local $keytable = &AssocVal($ALF_KeyTable{$table}, $key); local $keyoffset = &AssocVal($ALF_KeyOffset{$table},$key); local $x = $arg{$key}; local ($lower,$upper) = &ALF_FindInterpolationIndices($x,$keytable); local $x0 = &ArrayVal($keytable,$lower); local $x1 = &ArrayVal($keytable,$upper); local $lowerindex = $tableoffset+$keyoffset*$lower; local $upperindex = $tableoffset+$keyoffset*$upper; if ($ALF_Debug > 1) { print("# ALF Math Info:\t\tkey[$count]=$key\n"); print("# ALF Math Info:\t\tkeyoffset{$key}=$keyoffset\tkeytable{$key}=$keytable=(",join(' ',&Array($keytable)),")\n"); print("# ALF Math Info:\t\tx{$key}=$x\tx0=$keytable\[$lower\]=$x0\tx1=$keytable\[$upper\]=$x1\n"); } local ($y0,$y1); if ($count > 0) { $y0 = &ALF_EvaluateTable($table,$lowerindex,$count-1,%arg); $y1 = &ALF_EvaluateTable($table,$upperindex,$count-1,%arg); } else { $y0 = &ArrayVal($table,$lowerindex); $y1 = &ArrayVal($table,$upperindex); } local $y = $y0 + ($y1-$y0)*($x-$x0)/($x1-$x0); if ($ALF_Debug > 1) { print("# ALF Math Info:\t\ty($x)=$y\ty0=$table\[$lowerindex\]=$y0\ty1=$table\[$upperindex\]=$y1\n"); print("# ALF Math Info:\tfinished evaluation of ",$count+1,"D table $table\ttableoffset[$table]=$tableoffset\ty(",join(' ',%arg),")=$y\n") if ($count==$#key); } $y; } #################################### ### calculation graph processing ### #################################### sub ALF_Calculate { local($model,$method,%condition) = @_; $method = &LowerCase($method); print("# ALF Math Info:\tevaluating model\t",&ALF_ObjectInfoText($model),"\n") if $ALF_Debug; local $done = 1; local $result = $ALF_Result{$model}; if (defined($result)) { print("# ALF Math Info:\t$model is already evaluated\n") if $ALF_Debug; } else { local ($value,$header,$link,$record,$default); if (defined($value = $ALF_Value{$model})) { print("# ALF Math Info:\t$model is a trivial arithmetic model\n") if $ALF_Debug; $result = $value; } elsif ($header = &ALF_FindMatchingObject($model,'HEADER')) { local @keys = &Array($ALF_Children{$header}); print("# ALF Math Info:\t$model(@keys) is a full arithmetic model\n") if $ALF_Debug; local %arg; foreach $key (@keys) { local $keytype = $ALF_Type{$key}; local $keyname = $ALF_Name{$key}; local $keyid = $keyname? $keyname : $keytype; $arg{$keyid} = &ALF_Calculate($key,$method,%condition); } $result = &ALF_EvaluateArithmeticModel($model,%arg); } elsif ($link = $ALF_Link{$model}) { print("# ALF Math Info:\t$model is linked to another model $link\n") if $ALF_Debug; $result = &ALF_Calculate($link,$method,%condition); } elsif ($record = $ALF_Record{$model}) { local @data = &Array($record); print("# ALF Math Info:\t$model is linked to a data record $record = (@data)\n") if $ALF_Debug; local $eval = $ALF_EvaluationMethod{$record}; local $evalmethod = defined($eval)? $eval : $method; local @result = &ALF_EvaluateRecord($record,$method,%condition); $result = &Eval($evalmethod,@result); print("# ALF Math Info:\t$record evaluation result $result = $evalmethod(@result)\n") if $ALF_Debug; &AppendAssoc(join('.','ALF_Result',$method),$model,$result); } elsif ($default = &ALF_FindMatchingObject($model,'DEFAULT')) { print("# ALF Math Warning:\t$model evaluation result is default\n"); $result = $ALF_Value{$default}; } else { print("# ALF Math Error:\t$model cannot be evaluated\n"); } $ALF_Result{$model} = $result; } print("# ALF Math Info:\t$model evaluation result = \42$result\42\n") if $ALF_Debug; $result; } sub ALF_EvaluateRecord { local($record,$method,%condition) = @_; local ($model,@result); foreach $data (&Array($record)) { local $done; foreach $member (&Array($ALF_ResultList{$data})) { if (&ALF_ConditionMatch($member,%condition)) { $result[$#result+1] = $ALF_Result{$member}; $done = 1; } } if ($done) { print("# ALF Math Info:\t$data is already evaluated (@result)\n") if $ALF_Debug; } elsif ($model = $ALF_Link{$data}) { print("# ALF Math Info:\t$data is linked to a model $model\n") if $ALF_Debug; local $newresult = &ALF_Calculate($model,$method,%condition); $result[$#result+1] = $newresult; &ALF_UpdateRecord($data,$newresult,%condition); } else { print("# ALF Math Error:\t$data cannot be evaluated\n"); } } @result; } sub ALF_UpdateRecord { local($data,$newresult,%condition) = @_; local $newlistmember = &AutoName; $ALF_Result{$newlistmember} = $newresult; &AppendArray($ALF_ResultList{$data},$newlistmember); foreach $key (keys %condition) { local $condition = &LowerCase($condition{$key}); &AppendAssoc(join('.','ALF_Condition',&LowerCase($key)),$newlistmember,$condition); } $newlistmember; } sub ALF_AppendRecord { local($record,$link) = @_; local $data = &AutoName; &AppendArray($record,$data); $ALF_ResultList{$data} = &AutoName; $ALF_Link{$data} = $link if defined($link); $data; } sub ALF_ClearRecord { local($record) = @_; foreach $data (&Array($record)) { undef($ALF_Link{$data}); foreach $key (&Array($ALF_ResultList{$data})) { undef($ALF_Result{$key}); } &DeleteArray($ALF_ResultList{$data}); } &DeleteArray($record); } sub ALF_RecordInfoText { local($record,$comment,@condition) = @_; local @data = &Array($record); local @text; foreach $data (@data) { local $link = $ALF_Link{$data}; local @result; foreach $key (&Array($ALF_ResultList{$data})) { $result[$#result+1] = $ALF_Result{$key}; } # local @condition; # foreach $condition (@condition) { # local $key = join('.','ALF_Condition',&LowerCase($key)); # local $condition = &AssocVal($condition_key,$data); # $condition[$#condition+1] = "$condition_key:$condition" if defined($condition); # } $text[$#text+1] = "$comment\tresult($data) : (@result)"; # $text[$#text+1] = "$comment\tcondition($data) : (@condition)"; $text[$#text+1] = "$comment\tlink($data) :\t".&ALF_ObjectInfoText($link) if defined($link); } join("\n",@text); } sub ALF_ReportRecord { local($key,%condition) = @_; print(&ALF_HierarchyInfoText($key),"\t",&ALF_ObjectInfoText($key,'ALF_Record'),"\n"); print(&ALF_RecordInfoText($ALF_Record{$key},%condition),"\n"); } sub ALF_ConditionMatch { local($data,%condition) = @_; local $match = 1; foreach $key (keys %condition) { local $condition = &AssocVal(join('.','ALF_Condition',&LowerCase($key)),$data); if (defined($condition)) { $match = &StringMatch($condition,$condition{$key}); } last unless $match; } $match; } sub sum { local(@data) = @_; ($#data >= 0)? eval(join('+',@data)) : undef; } sub average { local(@data) = @_; local $n = $#data+1; ($n > 0)? eval(join('+',@data))/$n : undef; } sub minimum { local(@data) = @_; local $min = ($#data >= 0)? $data[0] : undef; foreach $data (@data) { $min = $data if ($data < $min); } $min; } sub maximum { local(@data) = @_; local $max = ($#data >= 0)? $data[0] : undef; foreach $data (@data) { $max = $data if ($data > $max); } $max; }