[system] / trunk / pg / lib / Parser / Differentiation.pm Repository:
ViewVC logotype

View of /trunk/pg/lib/Parser/Differentiation.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 6136 - (download) (as text) (annotate)
Sat Oct 3 16:40:47 2009 UTC (10 years, 2 months ago) by dpvc
File size: 18736 byte(s)
Handle formula 'constants' properly

    1 #
    2 #  Extend differentiation to multiple variables
    3 #  Check differentiation for complex functions
    4 #  Do derivatives for norm and unit.
    5 #
    6 #  Make shortcuts for getting numbers 1, 2, and sqrt, etc.
    7 #
    8 
    9 ##################################################
   10 #
   11 #  Differentiate the formula in terms of the given variable(s)
   12 #
   13 sub Parser::D {
   14   my $self = shift;
   15   my $d; my @x = @_; my $x;
   16   if (defined($x[0]) && $x[0] =~ m/^\d+$/) {
   17     $d = shift(@x);
   18     $self->Error("You can only specify one variable when you give a derivative count")
   19       unless scalar(@x) <= 1;
   20     return($self) if $d == 0;
   21   }
   22   if (scalar(@x) == 0) {
   23     my @vars = keys(%{$self->{variables}});
   24     my $n = scalar(@vars);
   25     if ($n == 0) {
   26       return $self->new('0') if $self->{isNumber};
   27       $x = 'x';
   28     } else {
   29       $self->Error("You must specify a variable to differentiate by") unless $n == 1;
   30       $x = $vars[0];
   31     }
   32     CORE::push(@x,$x);
   33   }
   34   @x = ($x[0]) x $d if $d;
   35   my $f = $self->{tree};
   36   foreach $x (@x) {
   37     return $self->new('0') unless defined $self->{variables}{$x};
   38     $f = $f->D($x);
   39   }
   40   return $self->new($f);
   41 }
   42 
   43 #
   44 #  Overridden by the classes that DO implement differentiation
   45 #
   46 sub Item::D {
   47   my $self = shift;
   48   my $type = ref($self); $type =~ s/.*:://;
   49   $self->Error("Differentiation for '%s' is not implemented",$type);
   50 }
   51 
   52 
   53 #########################################################################
   54 
   55 sub Parser::BOP::comma::D {Item::D(shift)}
   56 sub Parser::BOP::union::D {Item::D(shift)}
   57 
   58 sub Parser::BOP::add::D {
   59   my $self = shift; my $x = shift;
   60   $self = $self->Item("BOP")->new(
   61     $self->{equation},$self->{bop},
   62     $self->{lop}->D($x),$self->{rop}->D($x)
   63   );
   64   return $self->reduce;
   65 }
   66 
   67 
   68 sub Parser::BOP::subtract::D {
   69   my $self = shift; my $x = shift;
   70   $self = $self->Item("BOP")->new(
   71     $self->{equation},$self->{bop},
   72     $self->{lop}->D($x),$self->{rop}->D($x)
   73   );
   74   return $self->reduce;
   75 }
   76 
   77 sub Parser::BOP::multiply::D {
   78   my $self = shift; my $x = shift;
   79   my $equation = $self->{equation};
   80   my $BOP = $self->Item("BOP");
   81   $self =
   82     $BOP->new($equation,'+',
   83       $BOP->new($equation,$self->{bop},
   84         $self->{lop}->D($x),$self->{rop}->copy($equation)),
   85       $BOP->new($equation,$self->{bop},
   86         $self->{lop}->copy($equation),$self->{rop}->D($x))
   87     );
   88   return $self->reduce;
   89 }
   90 
   91 sub Parser::BOP::divide::D {
   92   my $self = shift; my $x = shift;
   93   my $equation = $self->{equation};
   94   my $BOP = $self->Item("BOP");
   95   $self =
   96     $BOP->new($equation,$self->{bop},
   97       $BOP->new($equation,'-',
   98         $BOP->new($equation,'*',
   99           $self->{lop}->D($x),$self->{rop}->copy($equation)),
  100         $BOP->new($equation,'*',
  101           $self->{lop}->copy($equation),$self->{rop}->D($x))
  102       ),
  103       $BOP->new($equation,'^',$self->{rop},$self->Item("Number")->new($equation,2))
  104     );
  105   return $self->reduce;
  106 }
  107 
  108 sub Parser::BOP::power::D {
  109   my $self = shift; my $x = shift;
  110   my $equation = $self->{equation};
  111   my $BOP = $self->Item("BOP");
  112   my $FN = $self->Item("Function");
  113   my $vars = $self->{rop}->getVariables;
  114   if (defined($vars->{$x})) {
  115     $vars = $self->{lop}->getVariables;
  116     if (defined($vars->{$x})) {
  117       $self =
  118         $FN->new($equation,'exp',
  119           [$BOP->new($equation,'*',$self->{rop}->copy($equation),
  120             $FN->new($equation,'ln',[$self->{lop}->copy($equation)],0))]);
  121        return $self->D($x);
  122     }
  123     $self = $BOP->new($equation,'*',
  124       $FN->new($equation,'ln',[$self->{lop}->copy($equation)],0),
  125       $BOP->new($equation,'*',$self->copy($equation),$self->{rop}->D($x))
  126     );
  127   } else {
  128     $self =
  129       $BOP->new($equation,'*',
  130         $BOP->new($equation,'*',
  131           $self->{rop}->copy($equation),
  132           $BOP->new($equation,$self->{bop},
  133             $self->{lop}->copy($equation),
  134             $BOP->new($equation,'-',
  135               $self->{rop}->copy($equation),
  136               $self->Item("Number")->new($equation,1)
  137             )
  138           )
  139         ),
  140         $self->{lop}->D($x)
  141       );
  142   }
  143   return $self->reduce;
  144 }
  145 
  146 sub Parser::BOP::cross::D      {Item::D(shift)}
  147 sub Parser::BOP::dot::D        {Item::D(shift)}
  148 sub Parser::BOP::underscore::D {Item::D(shift)}
  149 
  150 #########################################################################
  151 
  152 sub Parser::UOP::plus::D {
  153   my $self = shift; my $x = shift;
  154   return $self->{op}->D($x)
  155 }
  156 
  157 sub Parser::UOP::minus::D {
  158   my $self = shift; my $x = shift;
  159   $self = $self->Item("UOP")->new($self->{equation},'u-',$self->{op}->D($x));
  160   return $self->reduce;
  161 }
  162 
  163 sub Parser::UOP::factorial::D  {Item::D(shift)}
  164 
  165 #########################################################################
  166 
  167 sub Parser::Function::D {
  168   my $self = shift;
  169   $self->Error("Differentiation of '%s' not implemented",$self->{name});
  170 }
  171 
  172 sub Parser::Function::D_chain {
  173   my $self = shift; my $x = $self->{params}[0];
  174   my $name = "D_" . $self->{name};
  175   $self = $self->Item("BOP")->new($self->{equation},'*',$self->$name($x->copy),$x->D(shift));
  176   return $self->reduce;
  177 }
  178 
  179 #############################
  180 
  181 sub Parser::Function::trig::D {Parser::Function::D_chain(@_)}
  182 
  183 sub Parser::Function::trig::D_sin {
  184   my $self = shift; my $x = shift;
  185   return $self->Item("Function")->new($self->{equation},'cos',[$x]);
  186 }
  187 
  188 sub Parser::Function::trig::D_cos {
  189   my $self = shift; my $x = shift;
  190   my $equation = $self->{equation};
  191   return
  192     $self->Item("UOP")->new($equation,'u-',
  193       $self->Item("Function")->new($equation,'sin',[$x])
  194     );
  195 }
  196 
  197 sub Parser::Function::trig::D_tan {
  198   my $self = shift; my $x = shift;
  199   my $equation = $self->{equation};
  200   return
  201     $self->Item("BOP")->new($equation,'^',
  202       $self->Item("Function")->new($equation,'sec',[$x]),
  203       $self->Item("Number")->new($equation,2)
  204     );
  205 }
  206 
  207 sub Parser::Function::trig::D_cot {
  208   my $self = shift; my $x = shift;
  209   my $equation = $self->{equation};
  210   return
  211     $self->Item("UOP")->new($equation,'u-',
  212       $self->Item("BOP")->new($equation,'^',
  213         $self->Item("Function")->new($equation,'csc',[$x]),
  214         $self->Item("Number")->new($equation,2)
  215       )
  216     );
  217 }
  218 
  219 sub Parser::Function::trig::D_sec {
  220   my $self = shift; my $x = shift;
  221   my $equation = $self->{equation};
  222   my $FN = $self->Item("Function");
  223   return
  224     $self->Item("BOP")->new($equation,'*',
  225       $FN->new($equation,'sec',[$x]),
  226       $FN->new($equation,'tan',[$x])
  227     );
  228 }
  229 
  230 sub Parser::Function::trig::D_csc {
  231   my $self = shift; my $x = shift;
  232   my $equation = $self->{equation};
  233   my $FN = $self->Item("Function");
  234   return
  235     $self->Item("UOP")->new($equation,'u-',
  236       $self->Item("BOP")->new($equation,'*',
  237         $FN->new($equation,'csc',[$x]),
  238         $FN->new($equation,'cot',[$x])
  239       )
  240     );
  241 }
  242 
  243 sub Parser::Function::trig::D_asin {
  244   my $self = shift; my $x = shift;
  245   my $equation = $self->{equation};
  246   my $BOP = $self->Item("BOP");
  247   my $NUM = $self->Item("Number");
  248   return
  249     $BOP->new($equation,'/',
  250       $NUM->new($equation,1),
  251       $self->Item("Function")->new($equation,'sqrt',[
  252         $BOP->new($equation,'-',
  253           $NUM->new($equation,1),
  254           $BOP->new($equation,'^',$x,$NUM->new($equation,2))
  255         )]
  256       )
  257     );
  258 }
  259 
  260 sub Parser::Function::trig::D_acos {
  261   my $self = shift; my $x = shift;
  262   my $equation = $self->{equation};
  263   my $BOP = $self->Item("BOP");
  264   my $NUM = $self->Item("Number");
  265   return
  266     $self->Item("UOP")->new($equation,'u-',
  267       $BOP->new($equation,'/',
  268         $NUM->new($equation,1),
  269         $self->Item("Function")->new($equation,'sqrt',[
  270           $BOP->new($equation,'-',
  271             $NUM->new($equation,1),
  272             $BOP->new($equation,'^',$x,$NUM->new($equation,2))
  273           )]
  274         )
  275       )
  276     );
  277 }
  278 
  279 sub Parser::Function::trig::D_atan {
  280   my $self = shift; my $x = shift;
  281   my $equation = $self->{equation};
  282   my $BOP = $self->Item("BOP");
  283   my $NUM = $self->Item("Number");
  284   return
  285     $BOP->new($equation,'/',
  286       $NUM->new($equation,1),
  287       $BOP->new($equation,'+',
  288         $NUM->new($equation,1),
  289         $BOP->new($equation,'^',$x,$NUM->new($equation,2))
  290       )
  291     );
  292 }
  293 
  294 sub Parser::Function::trig::D_acot {
  295   my $self = shift; my $x = shift;
  296   my $equation = $self->{equation};
  297   my $BOP = $self->Item("BOP");
  298   my $NUM = $self->Item("Number");
  299   return
  300     $self->Item("UOP")->new($equation,'u-',
  301       $BOP->new($equation,'/',
  302         $NUM->new($equation,1),
  303         $BOP->new($equation,'+',
  304           $NUM->new($equation,1),
  305           $BOP->new($equation,'^',$x,$NUM->new($equation,2))
  306         )
  307       )
  308     );
  309 }
  310 
  311 sub Parser::Function::trig::D_asec {
  312   my $self = shift; my $x = shift;
  313   my $equation = $self->{equation};
  314   my $BOP = $self->Item("BOP");
  315   my $NUM = $self->Item("Number");
  316   my $FN = $self->Item("Function");
  317   return
  318     $BOP->new($equation,'/',
  319       $NUM->new($equation,1),
  320       $BOP->new($equation,'*',
  321         $FN->new($equation,'abs',[$x]),
  322         $FN->new($equation,'sqrt',[
  323           $BOP->new($equation,'-',
  324             $BOP->new($equation,'^',$x,$NUM->new($equation,2)),
  325             $NUM->new($equation,1)
  326           )]
  327         )
  328       )
  329     );
  330 }
  331 
  332 sub Parser::Function::trig::D_acsc {
  333   my $self = shift; my $x = shift;
  334   my $equation = $self->{equation};
  335   my $BOP = $self->Item("BOP");
  336   my $NUM = $self->Item("Number");
  337   my $FN = $self->Item("Function");
  338   return
  339     $self->Item("UOP")->new($equation,'u-',
  340       $BOP->new($equation,'/',
  341         $NUM->new($equation,1),
  342         $BOP->new($equation,'*',
  343           $FN->new($equation,'abs',[$x]),
  344           $FN->new($equation,'sqrt',[
  345             $BOP->new($equation,'-',
  346               $BOP->new($equation,'^',$x,$NUM->new($equation,2)),
  347               $NUM->new($equation,1)
  348             )]
  349           )
  350         )
  351       )
  352     );
  353 }
  354 
  355 
  356 #############################
  357 
  358 sub Parser::Function::hyperbolic::D {Parser::Function::D_chain(@_)}
  359 
  360 sub Parser::Function::hyperbolic::D_sinh {
  361   my $self = shift; my $x = shift;
  362   return $self->Item("Function")->new($self->{equation},'cosh',[$x]);
  363 }
  364 
  365 sub Parser::Function::hyperbolic::D_cosh {
  366   my $self = shift; my $x = shift;
  367   return $self->Item("Function")->new($self->{equation},'sinh',[$x]);
  368 }
  369 
  370 sub Parser::Function::hyperbolic::D_tanh {
  371   my $self = shift; my $x = shift;
  372   my $equation = $self->{equation};
  373   return
  374     $self->Item("BOP")->new($equation,'^',
  375       $self->Item("Function")->new($equation,'sech',[$x]),
  376       $self->Item("Number")->new($equation,2)
  377     );
  378 }
  379 
  380 sub Parser::Function::hyperbolic::D_coth {
  381   my $self = shift; my $x = shift;
  382   my $equation = $self->{equation};
  383   return
  384     $self->Item("UOP")->new($equation,'u-',
  385       $self->Item("BOP")->new($equation,'^',
  386         $self->Item("Function")->new($equation,'csch',[$x]),
  387         $self->Item("Number")->new($equation,2)
  388       )
  389     );
  390 }
  391 
  392 sub Parser::Function::hyperbolic::D_sech {
  393   my $self = shift; my $x = shift;
  394   my $equation = $self->{equation};
  395   my $FN = $self->Item("Function");
  396   return
  397     $self->Item("UOP")->new($equation,'u-',
  398       $self->Item("BOP")->new($equation,'*',
  399         $FN->new($equation,'sech',[$x]),
  400         $FN->new($equation,'tanh',[$x])
  401       )
  402     );
  403 }
  404 
  405 sub Parser::Function::hyperbolic::D_csch {
  406   my $self = shift; my $x = shift;
  407   my $equation = $self->{equation};
  408   my $FN = $self->Item("Function");
  409   return
  410     $self->Item("UOP")->new($equation,'u-',
  411       $self->Item("BOP")->new($equation,'*',
  412         $FN->new($equation,'csch',[$x]),
  413         $FN->new($equation,'coth',[$x])
  414       )
  415     );
  416 }
  417 
  418 sub Parser::Function::hyperbolic::D_asinh {
  419   my $self = shift; my $x = shift;
  420   my $equation = $self->{equation};
  421   my $BOP = $self->Item("BOP");
  422   my $NUM = $self->Item("Number");
  423   return
  424     $BOP->new($equation,'/',
  425       $NUM->new($equation,1),
  426       $self->Item("Function")->new($equation,'sqrt',[
  427         $BOP->new($equation,'+',
  428           $NUM->new($equation,1),
  429           $BOP->new($equation,'^',$x,$NUM->new($equation,2))
  430         )]
  431       )
  432     );
  433 }
  434 
  435 sub Parser::Function::hyperbolic::D_acosh {
  436   my $self = shift; my $x = shift;
  437   my $equation = $self->{equation};
  438   my $BOP = $self->Item("BOP");
  439   my $NUM = $self->Item("Number");
  440   return
  441     $BOP->new($equation,'/',
  442       $NUM->new($equation,1),
  443       $self->Item("Function")->new($equation,'sqrt',[
  444         $BOP->new($equation,'-',
  445           $BOP->new($equation,'^',$x,$NUM->new($equation,2)),
  446           $NUM->new($equation,1)
  447         )]
  448       )
  449     );
  450 }
  451 
  452 sub Parser::Function::hyperbolic::D_atanh {
  453   my $self = shift; my $x = shift;
  454   my $equation = $self->{equation};
  455   my $BOP = $self->Item("BOP");
  456   my $NUM = $self->Item("Number");
  457   return
  458     $BOP->new($equation,'/',
  459       $NUM->new($equation,1),
  460       $BOP->new($equation,'-',
  461         $NUM->new($equation,1),
  462         $BOP->new($equation,'^',$x,$NUM->new($equation,2))
  463       )
  464     );
  465 }
  466 
  467 sub Parser::Function::hyperbolic::D_acoth {
  468   my $self = shift; my $x = shift;
  469   my $equation = $self->{equation};
  470   my $BOP = $self->Item("BOP");
  471   my $NUM = $self->Item("Number");
  472   return
  473     $BOP->new($equation,'/',
  474       $NUM->new($equation,1),
  475       $BOP->new($equation,'-',
  476         $NUM->new($equation,1),
  477         $BOP->new($equation,'^',$x,$NUM->new($equation,2))
  478       )
  479     );
  480 }
  481 
  482 sub Parser::Function::hyperbolic::D_asech {
  483   my $self = shift; my $x = shift;
  484   my $equation = $self->{equation};
  485   my $BOP = $self->Item("BOP");
  486   my $NUM = $self->Item("Number");
  487   return
  488     $self->Item("UOP")->new($equation,'u-',
  489       $BOP->new($equation,'/',
  490         $NUM->new($equation,1),
  491         $BOP->new($equation,'*',
  492           $x,
  493           $self->Item("Function")->new($equation,'sqrt',[
  494             $BOP->new($equation,'-',
  495               $NUM->new($equation,1),
  496               $BOP->new($equation,'^',$x,$NUM->new($equation,2))
  497             )]
  498           )
  499         )
  500       )
  501     );
  502 }
  503 
  504 sub Parser::Function::hyperbolic::D_acsch {
  505   my $self = shift; my $x = shift;
  506   my $equation = $self->{equation};
  507   my $BOP = $self->Item("BOP");
  508   my $NUM = $self->Item("Number");
  509   my $FN = $self->Item("Function");
  510   return
  511     $self->Item("UOP")->new($equation,'u-',
  512       $BOP->new($equation,'/',
  513         $NUM->new($equation,1),
  514         $BOP->new($equation,'*',
  515           $FN->new($equation,'abs',[$x]),
  516           $FN->new($equation,'sqrt',[
  517             $BOP->new($equation,'+',
  518               $NUM->new($equation,1),
  519               $BOP->new($equation,'^',$x,$NUM->new($equation,2))
  520             )]
  521           )
  522         )
  523       )
  524     );
  525 }
  526 
  527 
  528 #############################
  529 
  530 sub Parser::Function::numeric::D {Parser::Function::D_chain(@_)}
  531 
  532 sub Parser::Function::numeric::D_ln {
  533   my $self = shift; my $x = shift;
  534   my $equation = $self->{equation};
  535   return $self->Item("BOP")->new($equation,'/',$self->Item("Number")->new($equation,1),$x);
  536 }
  537 
  538 sub Parser::Function::numeric::D_log {
  539   my $self = shift;
  540   my $base10 = $self->{equation}{context}{flags}{useBaseTenLog};
  541   if ($base10) {return $self->D_log10(@_)} else {return $self->D_ln(@_)}
  542 }
  543 
  544 sub Parser::Function::numeric::D_log10 {
  545   my $self = shift; my $x = shift;
  546   my $equation = $self->{equation};
  547   my $BOP = $self->Item("BOP");
  548   my $NUM = $self->Item("Number");
  549   return
  550     $BOP->new($equation,'/',
  551       $NUM->new($equation,1),
  552       $BOP->new($equation,'*',
  553         $NUM->new($equation,CORE::log(10)), $x
  554       )
  555     );
  556 }
  557 
  558 sub Parser::Function::numeric::D_exp {
  559   my $self = shift;
  560   return $self->copy();
  561 }
  562 
  563 sub Parser::Function::numeric::D_sqrt {
  564   my $self = shift;
  565   my $equation = $self->{equation};
  566   my $BOP = $self->Item("BOP");
  567   my $NUM = $self->Item("Number");
  568   return
  569     $BOP->new($equation,'/',
  570       $NUM->new($equation,1),
  571       $BOP->new($equation,'*',
  572         $NUM->new($equation,2),
  573         $self->copy
  574       )
  575     );
  576 }
  577 
  578 sub Parser::Function::numeric::D_abs {
  579   my $self = shift; my $x = shift;
  580   my $equation = $self->{equation};
  581   return $self->Item("BOP")->new($equation,'/',$x,$self->copy);
  582 }
  583 
  584 sub Parser::Function::numeric::D_int {Parser::Function::D(@_)}
  585 sub Parser::Function::numeric::D_sgn {Parser::Function::D(@_)}
  586 
  587 #########################################################################
  588 
  589 sub Parser::List::D {
  590   my $self = shift; my $x = shift;
  591   $self = $self->copy($self->{equation});
  592   foreach my $f (@{$self->{coords}}) {$f = $f->D($x)}
  593   return $self->reduce;
  594 }
  595 
  596 
  597 sub Parser::List::Interval::D {
  598   my $self = shift;
  599   $self->Error("Can't differentiate intervals");
  600 }
  601 
  602 sub Parser::List::AbsoluteValue::D {
  603   my $self = shift; my $x = $self->{coords}[0]->copy;
  604   my $equation = $self->{equation};
  605   my $BOP = $self->Item("BOP");
  606   return
  607     $BOP->new($equation,"*",
  608       $BOP->new($equation,'/', $x, $self->copy),
  609       $x->D(shift),
  610    );
  611 }
  612 
  613 
  614 #########################################################################
  615 
  616 sub Parser::Number::D {
  617   my $self = shift;
  618   $self->Item("Number")->new($self->{equation},0);
  619 }
  620 
  621 #########################################################################
  622 
  623 sub Parser::Complex::D {
  624   my $self = shift;
  625   $self->Item("Number")->new($self->{equation},0);
  626 }
  627 
  628 #########################################################################
  629 
  630 sub Parser::Constant::D {
  631   my $self = shift; my $x = shift;
  632   return $self->{def}{value}{tree}->D($x) if Value::isFormula($self->{def}{value});
  633   $self->Item("Number")->new($self->{equation},0);
  634 }
  635 
  636 #########################################################################
  637 
  638 sub Parser::Value::D {
  639   my $self = shift; my $x = shift; my $equation = $self->{equation};
  640   return $self->Item("Value")->new($equation,$self->{value}->D($x));
  641 }
  642 
  643 sub Value::D {
  644   my $self = shift; my $x = shift;
  645   my @coords = $self->value;
  646   foreach my $n (@coords)
  647     {if (ref($n) eq "") {$n = 0} else {$n = $n->D($x)->value}}
  648   return $self->new(@coords);
  649 }
  650 
  651 sub Value::List::D {
  652   my $self = shift; my $x = shift;
  653   my @coords = $self->value;
  654   foreach my $n (@coords)
  655     {if (ref($n) eq "") {$n = 0} else {$n = $n->D($x)}}
  656   return $self->new([@coords]);
  657 }
  658 
  659 sub Value::Interval::D {
  660   shift; shift; my $self = shift;
  661   $self->Error("Can't differentiate intervals");
  662 }
  663 
  664 sub Value::Set::D {
  665   shift; shift; my $self = shift;
  666   $self->Error("Can't differentiate sets");
  667 }
  668 
  669 sub Value::Union::D {
  670   shift; shift; my $self = shift;
  671   $self->Error("Can't differentiate unions");
  672 }
  673 
  674 #########################################################################
  675 
  676 sub Parser::Variable::D {
  677   my $self = shift; my $x = shift;
  678   my $d = ($self->{name} eq $x)? 1: 0;
  679   return $self->Item("Number")->new($self->{equation},$d);
  680 }
  681 
  682 #########################################################################
  683 
  684 sub Parser::String::D {
  685   my $self = shift;
  686   $self->Item("Number")->new($self->{equation},0);
  687 }
  688 
  689 #########################################################################
  690 
  691 package Parser::Differentiation;
  692 our $loaded = 1;
  693 
  694 #########################################################################
  695 
  696 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9