@Override
  public void scoreSearchToNode(
      Score score, Direction d, IConstraintMap c, IAttributeMap<? extends IAttribute> scoreAttrs) {
    Attribute attr = (Attribute) scoreAttrs.findAttr(scorerAttrId);
    if (attr == null) {
      return; // If we do not have the scorer attr present in the search direction, we do not score
              // - it wasn't 'wanted'
    }
    IBooleanValue bAttr = (IBooleanValue) attr;
    IAttributeConstraint na = c.findAttr(otherAttrId);

    // If there is no Node Data then we only score null
    if (na == null) { // || !na.hasValue()) {
      score.addNull(this, d);
      return;
    }

    float result = 0.0f;

    if (na.isIncludesNotSpecified()) {
      if (isScoreNull()) {
        result = getScoreOnNull();
      }
    }
    assert (na instanceof BooleanConstraint);
    result = Math.max(result, calcScore((BooleanConstraint) na, bAttr));
    score.add(this, result, d);
  }
 // FIXME Extract relevant bits from score()
 @Override
 public void scoreItemToItem(
     Score score,
     Score.Direction d,
     IAttributeMap<IAttribute> c,
     IAttributeMap<IAttribute> scoreAttrs) {
   IAttribute attr = scoreAttrs.findAttr(scorerAttrId);
   if (attr == null) {
     return; // If we do not have the scorer attr present in the search direction, we do not score
             // - it wasn't 'wanted'
   }
   score(score, d, attr, c, scoreAttrs);
 }
  // FIXME Extract relevant bits from score()
  @Override
  public void scoreSearchToNode(
      Score score,
      Score.Direction d,
      IConstraintMap c,
      IAttributeMap<? extends IAttribute> scoreAttrs) {
    assert (d == Direction.forwards); // Always the case

    IAttribute attr = scoreAttrs.findAttr(scorerAttrId);
    if (attr == null) {
      return; // If we do not have the scorer attr present in the search direction, we do not score
              // - it wasn't 'wanted'
    }
    score(score, d, attr, c);
  }
  @Override
  public void scoreNodeToSearch(
      Score score,
      Direction d,
      IAttributeMap<IAttributeConstraint> c,
      IAttributeMap<IAttribute> searchAttrs) {
    IAttributeConstraint na = c.findAttr(scorerAttrId);
    if (na == null) {
      return; // If we do not have the scorer attr present in the search direction, we do not score
              // - it wasn't 'wanted'
    }
    IAttributeConstraint bNa = na;
    IBooleanValue otherAttr = (IBooleanValue) searchAttrs.findAttr(otherAttrId);

    // If some nulls under this node then Score 1 so
    // as not to push this node down in score
    if (bNa.isIncludesNotSpecified()) {
      score.add(this, maxScore, d);
      return;
    }

    //        // This should never happen
    //        if (!bNa.hasValue()) {
    //            assert(false);
    //            return;
    //        }

    // If there is no Attr Data then we only score null
    if (otherAttr == null) {
      score.addNull(this, d);
      return;
    }

    float result = calcScore((BooleanConstraint) bNa, otherAttr);
    score.add(this, result, d);
  }
 // FIXME Extract relevant bits from score()
 @Override
 public void scoreNodeToSearch(
     Score score,
     Score.Direction d,
     IAttributeMap<IAttributeConstraint> c,
     IAttributeMap<IAttribute> searchAttrs) {
   assert (d == Direction.reverse); // Always the case
   IAttributeConstraint na = c.findAttr(scorerAttrId);
   if (na == null) {
     return; // If we do not have the scorer attr present in the search direction, we do not score
             // - it wasn't 'wanted'
   }
   if (!na.isIncludesNotSpecified()) {
     score(score, d, na, c, searchAttrs);
   }
 }
  public void score(
      Score score,
      Score.Direction d,
      IAttribute wantAttr,
      IAttributeMap<? extends IAttribute> c,
      IAttributeMap<? extends IAttribute> scoreAttrs) {
    assert (wantAttr.getAttrId() == scorerAttrId);

    if (wantAttr instanceof EcefVector) {
      // We must be scoring a search to an item or an Item to a search
      EcefVector want = (EcefVector) wantAttr;
      EcefVector location = (EcefVector) c.findAttr(otherAttrId);
      if (location != null) {

        float distance = location.distance(want); // actual distance in miles

        // Find out how far inside or outside the preferred range location is
        // 1 down to 0 is within range, and negative is outside
        float scoreFactor = 1f - (distance / range);

        // If preferClose is false, score any value within range as 1
        if (!preferClose && scoreFactor >= 0f) {
          scoreFactor = 1f;
        }

        float convertedScore = scoreMapper.getScore(scoreFactor);

        score.add(this, convertedScore, d);
        score.setScorerAttribute("Distance", distance);
      }
      return;
    } else {
      // scoring from a node to a search
      // Node attr is min/max of EcefVectors (x, y, z)
      // Attr is single point
      // Result is either: point is within possible ranges or not.
      // TODO: As 'preferCloser' is a function of the scorer, then can support both options.
      // 		If false, scoreFactor is either 1.0 or 0.0 within the range.
      // 			Outside of the range, something better is possible, based on the
      // 			largest value of 'range' within the node, and the closest point.
      // 		If true, ...
      // FIXME: Revise the above when brain is functioning, and then correct what is below.
      //		  Currently it may actually be correct!!

      DimensionsRangeConstraint node =
          (DimensionsRangeConstraint) wantAttr; // min/man of a 3D (x,y,z) want
      EcefVector location = (EcefVector) scoreAttrs.findAttr(otherAttrId);
      if (location == null) {
        return; // No matching attribute, so no scoring to do
      }

      float scoreFactor;
      Dimensions low = new Dimensions(node.getMin());

      Dimensions hi = new Dimensions(node.getMax());

      DimensionsRangeConstraint box = new DimensionsRangeConstraint(0, low, hi);
      if (box.consistent(location)) {
        // location is within range of x,y,z's of want
        scoreFactor = 1.0f;
      } else {
        // see if it is within range of closest point of box

        float distance = box.getDistance(location); // dist from
        // Find out how far inside or outside the preferred range location is
        // 1 down to 0 is within range, and negative is outside
        scoreFactor = 1f - (distance / range);
      }

      // If preferClose is false, score any value within range as 1
      if (!preferClose && scoreFactor >= 0f) {
        scoreFactor = 1f;
      }

      float convertedScore = scoreMapper.getScore(scoreFactor);
      score.add(this, convertedScore, d);
    }
  }