package AI::Fuzzy::Label;

## Fuzzy Label #### 
use overload (	'>'  => \&greaterthan,
		'<'  => \&lessthan,
		'>=' => \&greaterequal,
		'<=' => \&lessequal,
		'<=>'=> \&spaceship,
		'""'  => \&stringify 
	    );

sub new {
    my ($class, $name, $low, $mid, $high) = @_;
    my $self = {};

    bless $self, $class;

    $self->{name} = $name;
    $self->{low}  = $low;
    $self->{mid}  = $mid;
    $self->{high} = $high;

    return $self;
}

sub name {
    my ($self, $name) = @_;

    $self->{name} = $name if ($name);
    return $self->{name};
}

sub stringify {
    my $self=shift;
    return qq([$self->{name}: $self->{low},$self->{mid},$self->{high}]); 
}

sub lessthan {
    my ($self, $that) = @_;

    if ($self->{low} < $that->{low}) {
	return 1;
    } else {
	return 0;
    }
}

sub lessequal {
    my ($self, $that) = @_;

    if ($self->{low} <= $that->{low}) {
	return 1;
    } else {
	return 0;
    }
}

sub greaterthan {
    my ($self, $that) = @_;

    if ($self->{high} > $that->{high}) {
	return 1;
    } else {
	return 0;
    }
}

sub greaterequal {
    my ($self, $that) = @_;

    if ($self->{high} >= $that->{high}) {
	return 1;
    } else {
	return 0;
    }
}

sub between {
    my ($self, $that1, $that2) = @_;

    if ( ( $that1 <= $self and $self <= $that2) ||
	 ( $that2 <= $self and $self <= $that1) ) {
	return 1;
    } else {
	return 0;
    }
}

sub spaceship {
    my ($self, $that) = @_;

    return  ( $self->{mid} <=> $that->{mid} );
}

sub applicability {
    # this function should be called something else..
    # calculates to what degree this label applies to a $value

    my ($self, $value) = @_;
    my $membership = 0;

    # if the low and mid points are same as value, full membership
    # same if mid and high are same as value
    if ($self->{mid} == $self->{low} && $value == $self->{low}) { return 1 };  
    if ($self->{high} == $self->{mid} && $value == $self->{high}) { return 1 };  

    # m = slope of the line.. (change in y/change in x) 
    #     change in y is 1 as membership increases, -1 as it decreases
    my $mIncreasing =  1 / ($self->{mid} - $self->{low});
    my $mDecreasing = -1 / ($self->{high} - $self->{mid});

    # reject values that are "out of bounds"
    return ($membership = 0)
	if ($value <= $self->{low} ) or ($value >= $self->{high} );

    # now calculate membership:
    # y=mx+b , just like in algebra
    if ($value < $self->{mid}) {
	$membership = ($value - $self->{low}) * $mIncreasing;
    } elsif ($value == $self->{mid}) {
        $membership = 1;
    } else {
	$membership = (($value - $self->{mid}) * $mDecreasing) + 1;
    }
    
    return $membership;
}

sub range {
    # returns the distance from one endpoint to the other
    
    my ($self) = @_;
    return abs( $self->{high} - $self->{low} );
}

1;