@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);
  }
  /** Implementation that avoids creating any objects. Directly inspects the byte array */
  @Override
  public void scoreItemToItem(
      Score score,
      Direction d,
      IAttributeMap<IAttribute> otherAttrs,
      IAttributeMap<IAttribute> scoreAttrs) {
    // We assume that both are CompactAttrMap
    ByteArray scoreBytes = ((CompactAttrMap<?>) scoreAttrs).getByteArray();
    ByteArray otherBytes = ((CompactAttrMap<?>) otherAttrs).getByteArray();

    int scoreIndex = CompactAttrCodec.findAttrInBuf(scoreBytes, scorerAttrId);
    if (scoreIndex == CompactAttrCodec.NOT_FOUND) {
      return; // If we do not have the scorer attr present in the search direction, we do not score
              // - it wasn't 'wanted'
    }

    int otherIndex = CompactAttrCodec.findAttrInBuf(otherBytes, otherAttrId);

    if (otherIndex == CompactAttrCodec.NOT_FOUND) {
      score.addNull(this, d);
      return;
    }

    score.add(this, calcScore(scoreBytes, scoreIndex, otherBytes, otherIndex), d);
  }
  /* (non-Javadoc)
   * @see likemynds.db.indextree.Scorer#score(com.wwm.db.core.whirlwind.internal.IAttribute, likemynds.db.indextree.NodeAttributeContainer)
   */
  public void score(Score score, Score.Direction d, IAttribute wantAttr, IConstraintMap c) {

    IAttributeConstraint na = c.findAttr(otherAttrId);

    if (na == null) {
      return;
    }
    if (na
        .isIncludesNotSpecified()) { // na.hasValue() && // FIXME: Double check the logic this is
                                     // sometimes !includesNotSpecified()
      score.add(this, 1.0f, d);
      return;
    }
    //		if (!na.hasValue()) {
    //			return;
    //		}

    assert (wantAttr.getAttrId() == scorerAttrId);
    // score against bounding box
    EcefVector want = (EcefVector) wantAttr;
    DimensionsRangeConstraint lbv = (DimensionsRangeConstraint) na;
    if (lbv != null) {
      // if location is in box, there could be an exact match, so return 1.0f
      if (lbv.consistent(want)) {
        score.add(this, 1.0f, d);
        return;
      }

      // Else score based on distance from closest point of box, to the centre of this
      // RangePreference
      float distance = lbv.getDistance(want); // 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
      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);
      return;
    }
  }
  @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);
  }
  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);
    }
  }