# The LearningOnline Network with CAPA - LON-CAPA # QSet # # $Id: QSet.pm,v 1.2 2023/03/13 22:31:22 raeburn Exp $ # # Copyright (C) 2014 Michigan State University Board of Trustees # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # ## # A set of quantities ## package Apache::math_parser::QSet; use strict; use warnings; use utf8; use aliased 'Apache::math_parser::CalcException'; use aliased 'Apache::math_parser::Quantity'; use aliased 'Apache::math_parser::QSet'; use overload '""' => \&toString, '+' => \&union, '*' => \&qmult; ## # Constructor # @param {Quantity[]} quantities ## sub new { my $class = shift; # we use an array to preserve order (of course purely for cosmetic reasons) my $self = { _quantities => shift, }; bless $self, $class; # remove duplicates for (my $i=0; $i < scalar(@{$self->quantities}); $i++) { my $qi = $self->quantities->[$i]; for (my $j=0; $j < $i; $j++) { my $qj = $self->quantities->[$j]; if ($qi->equals($qj)) { splice(@{$self->quantities}, $i, 1); $i--; last; } } } return $self; } # Attribute helpers ## # The components of the set. # @returns {Quantity[]} ## sub quantities { my $self = shift; return $self->{_quantities}; } ## # Returns a readable view of the object # @returns {string} ## sub toString { my ( $self ) = @_; my $s = "{"; for (my $i=0; $i < scalar(@{$self->quantities}); $i++) { $s .= $self->quantities->[$i]->toString(); if ($i != scalar(@{$self->quantities}) - 1) { $s .= "; "; } } $s .= "}"; return $s; } ## # Equality test # @param {QSet} set # @optional {string|float} tolerance # @returns {boolean} ## sub equals { my ( $self, $set, $tolerance ) = @_; if (!$set->isa(QSet)) { return 0; } if (scalar(@{$self->quantities}) != scalar(@{$set->quantities})) { return 0; } foreach my $q1 (@{$self->quantities}) { my $found = 0; foreach my $q2 (@{$set->quantities}) { if ($q1->equals($q2, $tolerance)) { $found = 1; last; } } if (!$found) { return 0; } } return 1; } ## # Compare this set with another one, and returns a code. # Returns Quantity->WRONG_TYPE if the parameter is not a QSet. # @param {QSet|QInterval|Quantity|QVector|QMatrix} set # @optional {string|float} tolerance # @returns {int} Quantity->WRONG_TYPE|WRONG_DIMENSIONS|MISSING_UNITS|ADDED_UNITS|WRONG_UNITS|WRONG_VALUE|IDENTICAL ## sub compare { my ( $self, $set, $tolerance ) = @_; if (!$set->isa(QSet)) { return Quantity->WRONG_TYPE; } if (scalar(@{$self->quantities}) != scalar(@{$set->quantities})) { return Quantity->WRONG_DIMENSIONS; } my @codes = (); foreach my $q1 (@{$self->quantities}) { my $best_code = Quantity->WRONG_TYPE; foreach my $q2 (@{$set->quantities}) { my $code = $q1->compare($q2, $tolerance); if ($code == Quantity->IDENTICAL) { $best_code = $code; last; } elsif ($code > $best_code) { $best_code = $code; } } if ($best_code != Quantity->IDENTICAL) { return $best_code; } } return Quantity->IDENTICAL; } ## # Multiplication by a Quantity # @param {Quantity} q # @returns {QSet} ## sub qmult { my ( $self, $q ) = @_; if (!$q->isa(Quantity)) { die CalcException->new("Set multiplication: second member is not a quantity."); } my @t = (); foreach my $sq (@{$self->quantities}) { push(@t, $sq * $q); } return QSet->new(\@t); } ## # Union # @param {QSet} set # @returns {QSet} ## sub union { my ( $self, $set ) = @_; if (!$set->isa(QSet)) { die CalcException->new("Set union: second member is not a set."); } my @t = @{$self->quantities}; foreach my $q (@{$set->quantities}) { my $found = 0; foreach my $q2 (@t) { if ($q->equals($q2)) { $found = 1; last; } } if (!$found) { push(@t, $q); } } return QSet->new(\@t); } ## # Intersection # @param {QSet} set # @returns {QSet} ## sub intersection { my ( $self, $set ) = @_; if (!$set->isa(QSet)) { die CalcException->new("Set intersection: second member is not a set."); } my @t = (); foreach my $q (@{$self->quantities}) { my $found = 0; foreach my $q2 (@{$set->quantities}) { if ($q->equals($q2)) { $found = 1; last; } } if ($found) { push(@t, $q); } } return QSet->new(\@t); } ## # Equals # @param {Quantity|QVector|QMatrix|QSet|QInterval} set # @optional {string|float} tolerance # @returns {Quantity} ## sub qeq { my ( $self, $set, $tolerance ) = @_; my $q = $self->equals($set, $tolerance); return Quantity->new($q); } 1; __END__