anchor.diff:  
   
   [collectorsMinMax]
  -==== Minimums and maximums
  +===== Minimums and maximums
   
   To extract the minimum or maximum of a group, use the `ConstraintCollectors.min(...)` and
   `ConstraintCollectors.max(...)` collectors respectively.
  
positive.code: The return value for this collector is a 32-bit signed integer (`int`). There is also a 64-bit variant, `countLong()`.
  
  [collectorsSum]
  ==== Collecting `sum()`
  
  To sum the values of a particular property of all elements in the group, use the `ConstraintCollectors.sum(...)`
  collector. The following code snippet first groups all processes by the computer they run on and sums up all the power
  
negative.code: 
  // TODO Remove the previous sentence when PLANNER-1709 is fixed.
  
  
  [[constraintStreamsIntroduction]]
  == Introduction
  
  Using Java 8's Streams API, we could implement an <<easyJavaScoreCalculation,easy score calculator>>
  that uses a functional approach:
  
  [source,java,options="nowrap"]
  ----
      private int doNotAssignAnn() {
          int softScore = 0;
          schedule.getShiftList().stream()
                  .filter(Shift::isEmployeeAnn)
                  .forEach(shift -> {
                      softScore -= 1;
                  });
          return softScore;
      }
  ----
  
  However, that scales poorly because it doesn't do an <<incrementalScoreCalculation,incremental calculation>>:
  When the planning variable of a single `Shift` changes, to recalculate the score,
  the normal Streams API has to execute the entire stream from scratch.
  The ConstraintStreams API enables you to write similar code in pure Java, while reaping the performance benefits of
  incremental score calculation.
  This is an example of the same code, using the Constraint Streams API:
  
  // TODO Rewrite the above paragraph PLANNER-1709 is fixed, mentioning constraint justification as another benefit.
  
  [source,java,options="nowrap"]
  ----
      private Constraint doNotAssignAnn(ConstraintFactory factory) {
          return factory.from(Shift.class)
                  .filter(Shift::isEmployeeAnn)
                  .penalize("Don't assign Ann", HardSoftScore.ONE_SOFT);
      }
  ----
  
  This constraint stream iterates over all instances of class `Shift` in the <<problemFacts,problem facts>> and
  <<planningEntity,planning entities>> in the <<planningProblemAndPlanningSolution,planning problem>>.
  It finds every `Shift` which is assigned to employee `Ann` and for every such instance (also called a match), it adds a
  soft penalty of `1` to the overall <<calculateTheScore,score>>.
  The following figure illustrates this process on a problem with 4 different shifts:
  
  image::ConstraintStreams/constraintStreamIntroduction.png[align="center"]
  
  If any of the instances change during solving, the constraint stream automatically detects the change
  and only recalculates the minimum necessary portion of the problem that is affected by the change.
  The following figure illustrates this <<incrementalScoreCalculation,incremental score calculation>>:
  
  image::ConstraintStreams/constraintStreamIncrementalCalculation.png[align="center"]
  
  ////
  TODO Include and describe the following image when PLANNER-1709 is fixed.
  image::ConstraintStreams/constraintStreamJustification.png[align="center"]
  ////
  
  
  [[constraintStreamsConfiguration]]
  == Creating a constraint stream
  
  To use the ConstraintStreams API in your project, first write a pure Java `ConstraintProvider` implementation similar
  to the following example.
  
  [source,java,options="nowrap"]
  ----
      public class MyConstraintProvider implements ConstraintProvider {
  
          @Override
          public Constraint[] defineConstraints(ConstraintFactory factory) {
              return new Constraint[] {
                      penalizeEveryShift(factory)
              };
          }
  
          private Constraint penalizeEveryShift(ConstraintFactory factory) {
              return factory.from(Shift.class)
                  .penalize("Penalize a shift", HardSoftScore.ONE_SOFT);
          }
  
      }
  ----
  
  [NOTE]
  ====
  This example contains one constraint, `penalizeEveryShift(...)`.
  However, you can include as many as you require.
  ====
  
  Add the following code to your solver configuration:
  
  [source,xml,options="nowrap"]
  ----
  