public boolean evaluate(
        InternalWorkingMemory workingMemory,
        InternalReadAccessor leftExtractor,
        InternalFactHandle left,
        InternalReadAccessor rightExtractor,
        InternalFactHandle right) {
      final Object value1 = leftExtractor.getValue(workingMemory, left);
      final Object value2 = rightExtractor.getValue(workingMemory, right);

      Object target = value1;
      Object source = value2;

      return compare(source, target, workingMemory);
    }
    public boolean evaluate(
        InternalWorkingMemory workingMemory,
        final InternalReadAccessor extractor1,
        final Object object1,
        final InternalReadAccessor extractor2,
        final Object object2) {
      if (extractor1.isNullValue(workingMemory, object1)) {
        return false;
      }

      long obj1StartTS = -1;
      long obj1EndTS = -1;
      long obj2StartTS = -1;
      long obj2EndTS = -1;

      DefaultFactHandle obj1FH = (DefaultFactHandle) object1;

      if (obj1FH instanceof EventFactHandle) {
        obj1StartTS = ((EventFactHandle) obj1FH).getStartTimestamp();
        obj1EndTS = ((EventFactHandle) obj1FH).getEndTimestamp();
      } else {
        Object obj1Fact = workingMemory.getObject(obj1FH);
        if (obj1Fact instanceof SituationType) {
          obj1StartTS = ((SituationType) obj1Fact).getActivation().getTimestamp();
          if (!((SituationType) obj1Fact).isActive()) {
            obj1EndTS = ((SituationType) obj1Fact).getDeactivation().getTimestamp();
          }
        }
      }

      DefaultFactHandle obj2FH = (DefaultFactHandle) object2;

      if (obj2FH instanceof EventFactHandle) {
        obj2StartTS = ((EventFactHandle) obj2FH).getStartTimestamp();
        obj2EndTS = ((EventFactHandle) obj2FH).getEndTimestamp();
      } else {
        Object obj2Fact = workingMemory.getObject(obj2FH);
        if (obj2Fact instanceof SituationType) {
          obj2StartTS = ((SituationType) obj2Fact).getActivation().getTimestamp();
          // includes is not applicable when situationB is not finished
          if (!((SituationType) obj2Fact).isActive()) {
            obj2EndTS = ((SituationType) obj2Fact).getDeactivation().getTimestamp();
          } else return false;
        }
      }

      long distStart = obj2StartTS - obj1StartTS;
      if (obj1EndTS == (-1)) {
        return this.getOperator().isNegated()
            ^ (distStart >= this.startMinDev && distStart <= this.startMaxDev);
      } else {
        long distEnd = obj1EndTS - obj2EndTS;
        return this.getOperator().isNegated()
            ^ (distStart >= this.startMinDev
                && distStart <= this.startMaxDev
                && distEnd >= this.endMinDev
                && distEnd <= this.endMaxDev);
      }
    }
    public boolean evaluate(
        InternalWorkingMemory workingMemory,
        final InternalReadAccessor extractor1,
        final InternalFactHandle handle1,
        final InternalReadAccessor extractor2,
        final InternalFactHandle handle2) {
      if (extractor1.isNullValue(workingMemory, handle1.getObject())
          || extractor2.isNullValue(workingMemory, handle2.getObject())) {
        return false;
      }

      long distStart =
          ((EventFactHandle) handle1).getStartTimestamp()
              - ((EventFactHandle) handle2).getStartTimestamp();
      long distEnd =
          Math.abs(
              ((EventFactHandle) handle2).getEndTimestamp()
                  - ((EventFactHandle) handle1).getEndTimestamp());
      return this.getOperator().isNegated() ^ (distStart > 0 && distEnd <= this.endDev);
    }
    /** @inheridDoc */
    public boolean evaluate(
        InternalWorkingMemory workingMemory,
        InternalReadAccessor extractor,
        InternalFactHandle handle,
        FieldValue value) {
      final Object objectValue = extractor.getValue(workingMemory, handle.getObject());
      final Object literal = value.getValue();
      if (cachedValue != literal) {
        cachedValue = literal;
        cacheLiteral(literal, workingMemory);
      }

      TraitableBean core;
      if (objectValue instanceof Thing) {
        Thing thing = (Thing) objectValue;
        core = (TraitableBean) thing.getCore();
        BitSet code = core.getCurrentTypeCode();
        if (code != null) {
          return this.getOperator().isNegated() ^ isA(code, cachedLiteral);
        } else {
          return this.getOperator().isNegated() ^ hasTrait(core, literal);
        }
      } else if (objectValue instanceof TraitableBean) {
        core = (TraitableBean) objectValue;
        BitSet code = core.getCurrentTypeCode();
        if (code != null) {
          return this.getOperator().isNegated() ^ isA(code, cachedLiteral);
        } else {
          return this.getOperator().isNegated() ^ hasTrait(core, literal);
        }
      } else {
        core = lookForWrapper(objectValue, workingMemory);
        if (core == null) {
          return this.getOperator().isNegated();
        }
        BitSet code = core.getCurrentTypeCode();
        if (code != null) {
          return this.getOperator().isNegated() ^ isA(code, cachedLiteral);
        } else {
          return this.getOperator().isNegated() ^ hasTrait(core, literal);
        }
      }
    }