File:  [LON-CAPA] / loncom / homework / math_parser / QSet.pm
Revision 1.2: download - view: text, annotated - select for diffs
Mon Mar 13 22:31:22 2023 UTC (21 months, 2 weeks ago) by raeburn
Branches: MAIN
CVS tags: version_2_12_X, version_2_11_5_msu, version_2_11_4_msu, HEAD
- Add $Id$ line in comments for display of version.

# 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 <http://www.gnu.org/licenses/>.
#

##
# 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__

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>