#!/usr/bin/perl -w

use strict;

my ($input, $ok);
my %brain;
my @relations=qw/some all no somenot/;

while (1) {
  print "> ";
  $input = <STDIN>;
  chomp $input;
  
  if ($input =~ /^\s*are\s+(\w+)\s+([\w\s]+)\s*\?\s*$/i) {
    &analyse_as_question(($1 eq "any")? "some": $1,$2); #we have stored the relations: all some somenot no. So "any" becomes "some". The case "Are any PLURAL_NOUN not PLURAL_NOUN is considered inside the sub.
  }
  elsif ($input =~ /^\s*describe ([\w\s]+)\s*\.?\s*$/i) {
    &describe($1);
  }
  
  elsif ($input =~ /^\s*(\w+)\s+([\w\s]+)\s+are\s+((?:not)?)\s*([\w\s]+)\.?\s*$/i) {
    
    $ok = &remember($1 . $3,$2,$4); #Remember what he said and
    &remember((lc($1 . $3) eq "all")? "some" : $1 . $3, $4, $2) if ($ok && ($3 ne "not")); # Infer in the other direction: "All dogs are mammals". Then, "_Some_ mammals are dogs".
    print "OK.\n" if $ok;
  }

  else {
    print "I don't understand that.\n";
  }

}

sub analyse_as_question {
  
  my $relation = shift;
  my $nouns = shift;
  my @words = split(/ /,$nouns);
  my $first_noun;
  my $second_noun;
  my @known_nouns;

  @known_nouns=keys(%brain);
  map {s/-.*//} @known_nouns; 

  ($first_noun,$second_noun) = split(/ not /,$nouns); #We can easily split the two words if the question is "Are any PLURAL_NOUN not PLURAL_NOUN?
  if (defined $second_noun) { #there was a not.
    $relation="somenot";
  }
  else { #there was no not. We will have to guess.
    for (my $i=0;$i<@words-1;$i++) {
      $first_noun=join (" ",@words[0..$i]);
      if (grep(/^$first_noun$/,@known_nouns)) {
          $second_noun = join " ",@words[$i+1..$#words];
        last;
      }
    }
  }
  
  unless (defined($second_noun) and grep(/^$second_noun$/,@known_nouns)) { #Did we guess it correctly?
    print "I don't know anything about this.\n";
    return;
  }

  my $relation_found = &look_for($second_noun, $first_noun, $second_noun);
  # $relation -> $relation_found: answer
  # all->all          that's right!   
  # some->some        that's right!   
  # somenot->somenot  that's right!   
  # no->no            that's right!   
  # all->some         no, some are    
  # all->somenot      no, not all     
  # all->no           no, not all     
  # some->all         yes, all
  # some->somenot     no, not all     
  # some->no          no, not all     
  # somenot->all      no, all
  # somenot->some     no, some are    
  # somenot->no       no, not all     
  # no->all           no, all
  # no->some          no, some        
  # no->somenot       no, not all     
  if ($relation_found eq $relation) {
    if ($relation eq "somenot") {
      print "Yes, some $first_noun are not $second_noun.\n";
    }
    else {
      print "Yes, $relation $first_noun are $second_noun.\n";
    }
  }
  elsif ($relation_found eq "some") {
    print "No, some $first_noun are $second_noun.\n";
  }
  elsif (($relation_found eq "somenot") or ($relation_found eq "no")) {
    print "No, not all $first_noun are $second_noun.\n";
  }
  elsif ($relation_found eq "all") {
    if ($relation eq "some") {
      print "Yes, all cats are dogs.\n";
    }
    else {
      print "No, all $first_noun are $second_noun.\n";
    }
  }
  elsif ($relation_found eq "Dead end") {
    print "I don't know.\n";
  }
  
}

sub describe {
  
  my $noun=shift;
  my $i_know_something;
  my @known_things;
  my @seen;
  my $relation;

  @known_things = keys(%brain); 
  map {s/-.*//} @known_things;
  
  foreach my $concept (@known_things) {
    next if ((grep(/^$concept$/,@seen)) or ($concept eq $noun));
    $relation = &look_for($concept,$noun,$concept);
    next if $relation eq "Dead end";
    if ($relation eq "somenot") {
        print ucfirst "Some $noun are not $concept.\n";
    }
    else {
      print ucfirst "$relation $noun are $concept.\n";
    }
    $i_know_something=1;
    push(@seen,$concept); #so we don't repeat.
  }

  print "I know nothing about $noun.\n" unless $i_know_something;
  
}

sub remember {

  
  my $relation = lc shift;
  my $first_noun = lc shift;
  my $second_noun = lc shift;

  unless (grep(/^$relation$/, @relations)) {
    print "Syntax error: I don't understand $relation\n";
    return ;
  }

  $brain{"$first_noun-$relation"} = [] unless exists $brain{"$first_noun-$relation"};
  
  #look_for contradictions or already known statements
  my $found=&look_for($second_noun,$first_noun,$second_noun);

  if ($found ne "Dead end") {
    print "I know.\n" if ($found eq $relation);
    print "Sorry, that contradicts what I already know.\n" if ($found ne $relation);
    return;
  }

  push @{$brain{"$first_noun-$relation"}}, $second_noun; 
  return 1; #A non-zero return means it was OK.
}

sub look_for {

  my $what = shift;
  my $where = shift;
  my @already_seen=@_;
  my $relation;
  my $stat;
  my $element;

  foreach $relation (@relations) {
    return $relation if (grep(/^$what$/,@{$brain{"$where-$relation"}})); #if what we are finding is here, return.
    foreach $element (@{$brain{"$where-$relation"}}) { #seek inside each element
      next if (grep(/^$element$/,@already_seen));
      $stat = &look_for($what,$element,@already_seen,$where); #We append $where to the ones that we have already seen to avoid short circuits.
      next if($stat eq "Dead end");
      #If we have reach this point, we have found something: let's see what we return: (next means we don't have enough information)
      ################################################################################
      #$relation -> $stat : what we return
      #all->all         all
      #all->some        next
      #all->somenot     next
      #all->no          no
      #some->all        some
      #some->some       next
      #some->somenot    next
      #some->no         somenot
      #somenot->all     somenot
      #somenot->some    next
      #somenot->somenot next
      #somenot->no      next
      #no->all          next
      #no->some         next
      #no->somenot      next
      #no->no           next
      if ($relation eq "all") {
        return $stat if (($stat eq "all") or ($stat eq "no"));
        next;
      }
      elsif ($relation eq "some") {
        return "some"  if ($stat eq "all");
        return "somenot" if ($stat eq "no");
        next;
      }
      elsif ($relation eq "somenot") {
        return "somenot" if ($stat eq "all");
        next;
      }
      elsif ($relation eq "no") { #OK, this is redundant (as some of the elsif's up there), but I think it clarifies the whole thing.
        next;
      }
    }
  }


  return "Dead end"; #We found nothing
}
