View Javadoc
1   package org.opentrafficsim.core.parameters;
2   
3   import java.io.Serializable;
4   import java.util.ArrayList;
5   import java.util.Collections;
6   import java.util.HashMap;
7   import java.util.Iterator;
8   import java.util.LinkedHashMap;
9   import java.util.LinkedHashSet;
10  import java.util.List;
11  import java.util.Map;
12  import java.util.Set;
13  
14  import org.djunits.unit.Unit;
15  import org.djunits.value.vdouble.scalar.AbstractDoubleScalarRel;
16  import org.opentrafficsim.base.parameters.ParameterException;
17  import org.opentrafficsim.base.parameters.ParameterType;
18  import org.opentrafficsim.base.parameters.ParameterTypeDouble;
19  import org.opentrafficsim.base.parameters.ParameterTypeNumeric;
20  import org.opentrafficsim.base.parameters.Parameters;
21  import org.opentrafficsim.core.gtu.GTUType;
22  import org.opentrafficsim.core.units.distributions.ContinuousDistDoubleScalar;
23  
24  import nl.tudelft.simulation.jstats.distributions.DistContinuous;
25  import nl.tudelft.simulation.jstats.distributions.DistDiscrete;
26  
27  /**
28   * Sets parameter values based on the the GTU type. This includes stochastic parameters. Parameters may also be defined for all
29   * GTU types. Similarly, correlations between two parameters can be determined, for all or a specific GTU type.
30   * <p>
31   * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
32   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
33   * <p>
34   * @version $Revision$, $LastChangedDate$, by $Author$, initial version 18 nov. 2016 <br>
35   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
36   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
37   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
38   */
39  public class ParameterFactoryByType implements ParameterFactory
40  {
41  
42      /** Parameters. */
43      private final Map<GTUType, Set<ParameterEntry<?>>> map = new HashMap<>();
44  
45      /** Map of correlations. */
46      private Map<GTUType, Map<ParameterType<?>, Map<ParameterType<?>, Correlation<?, ?>>>> correlations = new HashMap<>();
47  
48      /** {@inheritDoc} */
49      @Override
50      public void setValues(final Parameters parameters, final GTUType gtuType) throws ParameterException
51      {
52          // set of parameters that this class is going to set
53          Map<ParameterType<?>, ParameterEntry<?>> setType = new LinkedHashMap<>();
54          List<GTUType> gtuTypes = new ArrayList<>();
55          gtuTypes.add(gtuType);
56          GTUType parent = gtuType;
57          do
58          {
59              parent = parent.getParent();
60              gtuTypes.add(parent);
61          }
62          while (parent != null);
63          Collections.reverse(gtuTypes);
64          for (GTUType type : gtuTypes)
65          {
66              if (this.map.containsKey(type))
67              {
68                  for (ParameterEntry<?> entry : this.map.get(type))
69                  {
70                      setType.put(entry.getParameterType(), entry);
71                  }
72              }
73          }
74  
75          /* {@formatter:off}
76           * Based on all given correlations we create two maps: 
77           * - remainingCorrelations, keys are ParameterTypes that depend on values that this class still needs to set 
78           * - allCorrelations, correlations combined from all and the specific GTU type, used to actually alter the values set
79           * The map remainingCorrelations will only contain correlations to parameters also defined in this class. For other 
80           * correlations the independent parameter should already be present in the input Parameters set. If it's not, an 
81           * exception follows as the parameter could not be retrieved. Circular dependencies are recognized as a loop has not 
82           * done anything, while remainingCorrelations is not empty (i.e. some parameters should still be set, but can't as they 
83           * all depend on parameters not yet set). {@formatter:on}
84           */
85          Map<ParameterType<?>, Map<ParameterType<?>, Correlation<?, ?>>> remainingCorrelations = new LinkedHashMap<>();
86          Map<ParameterType<?>, Map<ParameterType<?>, Correlation<?, ?>>> allCorrelations = new LinkedHashMap<>();
87          for (GTUType type : gtuTypes) // null first, so specific type overwrites
88          {
89              if (this.correlations.containsKey(type))
90              {
91                  Map<ParameterType<?>, Map<ParameterType<?>, Correlation<?, ?>>> map1 = this.correlations.get(type);
92                  for (ParameterType<?> then : map1.keySet())
93                  {
94                      Map<ParameterType<?>, Correlation<?, ?>> map2a = map1.get(then);
95                      Map<ParameterType<?>, Correlation<?, ?>> map2b = new LinkedHashMap<>(map2a); // safe copy
96                      // retain only independent correlation parameters this class will set
97                      map2b.keySet().retainAll(setType.keySet());
98                      if (!map2b.isEmpty())
99                      {
100                         Map<ParameterType<?>, Correlation<?, ?>> map3 = remainingCorrelations.get(then);
101                         if (map3 == null)
102                         {
103                             map3 = new LinkedHashMap<>();
104                             remainingCorrelations.put(then, map3);
105                         }
106                         map3.putAll(map2b);
107                     }
108                     if (!map2a.isEmpty())
109                     {
110                         Map<ParameterType<?>, Correlation<?, ?>> map3 = allCorrelations.get(then);
111                         if (map3 == null)
112                         {
113                             map3 = new LinkedHashMap<>();
114                             allCorrelations.put(then, map3);
115                         }
116                         map3.putAll(map2a);
117                     }
118                 }
119             }
120         }
121 
122         // loop and set parameters that do not correlate to parameters not yet set
123         if (!setType.keySet().containsAll(allCorrelations.keySet()))
124         {
125             Set<ParameterType<?>> params = new LinkedHashSet<>(allCorrelations.keySet());
126             params.removeAll(setType.keySet());
127             throw new ParameterException("Parameters " + params
128                     + " depend on a correlation, but are not added through addParameter() to set a base value.");
129         }
130         boolean altered = true;
131         while (altered)
132         {
133             altered = false;
134 
135             Iterator<ParameterType<?>> iterator = setType.keySet().iterator();
136             while (iterator.hasNext())
137             {
138                 ParameterType<?> parameterType = iterator.next();
139                 ParameterEntry<?> entry = setType.get(parameterType);
140 
141                 if (!remainingCorrelations.containsKey(parameterType))
142                 {
143                     altered = true;
144                     iterator.remove();
145                     Object value = entry.getValue();
146                     setParameter(parameterType, value, parameters, allCorrelations.get(parameterType));
147                     // remove the set parameter from correlations that need to be considered
148                     Iterator<ParameterType<?>> it = remainingCorrelations.keySet().iterator();
149                     while (it.hasNext())
150                     {
151                         Map<ParameterType<?>, Correlation<?, ?>> remMap = remainingCorrelations.get(it.next());
152                         remMap.remove(parameterType);
153                         if (remMap.isEmpty())
154                         {
155                             it.remove(); // all independent parameters were set, remove correlation to consider
156                         }
157                     }
158                 }
159             }
160         }
161         if (!altered && !remainingCorrelations.isEmpty())
162         {
163             throw new RuntimeException("Circular correlation between parameters.");
164         }
165 
166     }
167 
168     /**
169      * Sets a parameter including type casting.
170      * @param parameterType ParameterType&lt;?&gt;; parameter type
171      * @param value Object; value
172      * @param parameters Parameters; parameters to set in
173      * @param correls Map&lt;ParameterType&lt;?&gt;, Correlation&lt;?, ?&gt;&gt;; correlations
174      * @param <C> parameter value type of first parameter
175      * @param <T> parameter value type of then parameter
176      */
177     @SuppressWarnings("unchecked")
178     private <C, T> void setParameter(final ParameterType<?> parameterType, final Object value, final Parameters parameters,
179             final Map<ParameterType<?>, Correlation<?, ?>> correls)
180     {
181         T val = (T) value;
182         try
183         {
184             if (correls != null)
185             {
186                 for (ParameterType<?> param : correls.keySet())
187                 {
188                     Correlation<C, T> correlation = (Correlation<C, T>) correls.get(param);
189                     if (param == null)
190                     {
191                         val = correlation.correlate(null, val);
192                     }
193                     else
194                     {
195                         val = correlation.correlate(parameters.getParameter((ParameterType<C>) param), val);
196                     }
197                 }
198             }
199             parameters.setParameter((ParameterType<T>) parameterType, val);
200         }
201         catch (ParameterException exception)
202         {
203             throw new RuntimeException("Value out of bounds or dependent parameter not present.", exception);
204         }
205     }
206 
207     /**
208      * @param gtuType GTUType; the gtu type
209      * @param parameterType ParameterType&lt;T&gt;; the parameter type
210      * @param value T; the value of the parameter
211      * @param <T> parameter value type
212      */
213     public <T> void addParameter(final GTUType gtuType, final ParameterType<T> parameterType, final T value)
214     {
215         assureTypeInMap(gtuType);
216         this.map.get(gtuType).add(new FixedEntry<>(parameterType, value));
217     }
218 
219     /**
220      * @param gtuType GTUType; the gtu type
221      * @param parameterType ParameterTypeNumeric&lt;T&gt;; the parameter type
222      * @param distribution ContinuousDistDoubleScalar.Rel&lt;T,U&gt;; the distribution of the parameter
223      * @param <U> unit type
224      * @param <T> parameter value type
225      */
226     public <U extends Unit<U>, T extends AbstractDoubleScalarRel<U, T>> void addParameter(final GTUType gtuType,
227             final ParameterTypeNumeric<T> parameterType, final ContinuousDistDoubleScalar.Rel<T, U> distribution)
228     {
229         assureTypeInMap(gtuType);
230         this.map.get(gtuType).add(new DistributedEntry<>(parameterType, distribution));
231     }
232 
233     /**
234      * @param gtuType GTUType; the gtu type
235      * @param parameterType ParameterTypeInteger; the parameter type
236      * @param distribution DistDiscrete; the distribution of the parameter
237      */
238     public void addParameter(final GTUType gtuType, final ParameterType<Integer> parameterType, final DistDiscrete distribution)
239     {
240         assureTypeInMap(gtuType);
241         this.map.get(gtuType).add(new DistributedEntryInteger(parameterType, distribution));
242     }
243 
244     /**
245      * @param gtuType GTUType; the gtu type
246      * @param parameterType ParameterTypeDouble; the parameter type
247      * @param distribution DistContinuous; the distribution of the parameter
248      */
249     public void addParameter(final GTUType gtuType, final ParameterType<Double> parameterType,
250             final DistContinuous distribution)
251     {
252         assureTypeInMap(gtuType);
253         this.map.get(gtuType).add(new DistributedEntryDouble(parameterType, distribution));
254     }
255 
256     /**
257      * Add parameter for all GTU types.
258      * @param parameterType ParameterType&lt;T&gt;; the parameter type
259      * @param value T; the value of the parameter
260      * @param <T> type
261      */
262     public <T> void addParameter(final ParameterType<T> parameterType, final T value)
263     {
264         addParameter(null, parameterType, value);
265     }
266 
267     /**
268      * Add parameter for all GTU types.
269      * @param parameterType ParameterTypeDouble; the parameter type
270      * @param value double; the value of the parameter
271      */
272     public void addParameter(final ParameterTypeDouble parameterType, final double value)
273     {
274         addParameter(null, parameterType, value);
275     }
276 
277     /**
278      * Add parameter for all GTU types.
279      * @param parameterType ParameterTypeNumeric&lt;T&gt;; the parameter type
280      * @param distribution ContinuousDistDoubleScalar.Rel&lt;T,U&gt;; the distribution of the parameter
281      * @param <U> unit type
282      * @param <T> parameter value type
283      */
284     public <U extends Unit<U>, T extends AbstractDoubleScalarRel<U, T>> void addParameter(
285             final ParameterTypeNumeric<T> parameterType, final ContinuousDistDoubleScalar.Rel<T, U> distribution)
286     {
287         addParameter(null, parameterType, distribution);
288     }
289 
290     /**
291      * Add parameter for all GTU types.
292      * @param parameterType ParameterTypeDouble; the parameter type
293      * @param distribution DistContinuous; the distribution of the parameter
294      */
295     public void addParameter(final ParameterTypeDouble parameterType, final DistContinuous distribution)
296     {
297         addParameter(null, parameterType, distribution);
298     }
299 
300     /**
301      * Correlates one parameter to another. The parameter 'first' may also be {@code null}, in which case the parameter can be
302      * correlated to an external source.
303      * @param gtuType GTUType; GTU type
304      * @param first ParameterType&lt;C&gt;; independent parameter
305      * @param then ParameterType&lt;T&gt;; dependent parameter
306      * @param correlation Correlation&lt;C, T&gt;; correlation
307      * @param <C> parameter value type of first parameter
308      * @param <T> parameter value type of then parameter
309      */
310     public <C, T> void addCorrelation(final GTUType gtuType, final ParameterType<C> first, final ParameterType<T> then,
311             final Correlation<C, T> correlation)
312     {
313         assureTypeInMap(gtuType);
314         Map<ParameterType<?>, Map<ParameterType<?>, Correlation<?, ?>>> map1 = this.correlations.get(gtuType);
315         Map<ParameterType<?>, Correlation<?, ?>> map2 = map1.get(then);
316         if (map2 == null)
317         {
318             map2 = new LinkedHashMap<>();
319             map1.put(then, map2);
320         }
321         map2.put(first, correlation);
322     }
323 
324     /**
325      * Correlates one parameter to another for all GTU types.
326      * @param first ParameterType&lt;C&gt;; independent parameter
327      * @param then ParameterType&lt;T&gt;; dependent parameter
328      * @param correlation Correlation&lt;C, T&gt;; correlation
329      * @param <C> parameter value type of first parameter
330      * @param <T> parameter value type of then parameter
331      */
332     public <C, T> void addCorrelation(final ParameterType<C> first, final ParameterType<T> then,
333             final Correlation<C, T> correlation)
334     {
335         addCorrelation(null, first, then, correlation);
336     }
337 
338     /**
339      * Assures the gtu type is in the map.
340      * @param gtuType GTUType; the gtu type
341      */
342     private void assureTypeInMap(final GTUType gtuType)
343     {
344         if (!this.map.containsKey(gtuType))
345         {
346             this.map.put(gtuType, new LinkedHashSet<>());
347             this.correlations.put(gtuType, new LinkedHashMap<>());
348         }
349     }
350 
351     /** {@inheritDoc} */
352     @Override
353     public String toString()
354     {
355         return "ParameterFactoryByType [map=" + this.map + "]";
356     }
357 
358     /**
359      * Local storage interface for parameters.
360      * <p>
361      * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
362      * <br>
363      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
364      * <p>
365      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 19 nov. 2016 <br>
366      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
367      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
368      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
369      * @param <T> value type
370      */
371     private interface ParameterEntry<T>
372     {
373         /**
374          * Returns the value for parameter.
375          * @return T; value for the parameter
376          */
377         T getValue();
378 
379         /**
380          * Returns the parameter type.
381          * @return ParameterType; parameter type
382          */
383         ParameterType<T> getParameterType();
384     }
385 
386     /**
387      * Fixed parameter.
388      * <p>
389      * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
390      * <br>
391      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
392      * <p>
393      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 19 nov. 2016 <br>
394      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
395      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
396      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
397      * @param <T> value type
398      */
399     private final class FixedEntry<T> implements ParameterEntry<T>, Serializable
400     {
401         /** */
402         private static final long serialVersionUID = 20170400L;
403 
404         /** Parameter type. */
405         private final ParameterType<T> parameterType;
406 
407         /** Value. */
408         private final T value;
409 
410         /**
411          * @param parameterType ParameterType&lt;T&gt;; the parameter type
412          * @param value T; the fixed value
413          */
414         FixedEntry(final ParameterType<T> parameterType, final T value)
415         {
416             this.parameterType = parameterType;
417             this.value = value;
418         }
419 
420         /** {@inheritDoc} */
421         @Override
422         public T getValue()
423         {
424             return this.value;
425         }
426 
427         /** {@inheritDoc} */
428         @Override
429         public ParameterType<T> getParameterType()
430         {
431             return this.parameterType;
432         }
433 
434         /** {@inheritDoc} */
435         @Override
436         public String toString()
437         {
438             return "FixedEntry [parameterType=" + this.parameterType + ", value=" + this.value + "]";
439         }
440 
441     }
442 
443     /**
444      * Distributed parameter.
445      * <p>
446      * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
447      * <br>
448      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
449      * <p>
450      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 2 mrt. 2018 <br>
451      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
452      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
453      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
454      * @param <U> unit type
455      * @param <T> value type
456      */
457     private final class DistributedEntry<U extends Unit<U>, T extends AbstractDoubleScalarRel<U, T>>
458             implements ParameterEntry<T>, Serializable
459     {
460         /** */
461         private static final long serialVersionUID = 20180203L;
462 
463         /** Parameter type. */
464         private final ParameterType<T> parameterType;
465 
466         /** Distribution of the parameter. */
467         private final ContinuousDistDoubleScalar.Rel<T, U> distribution;
468 
469         /**
470          * @param parameterType ParameterType&lt;T&gt;; the parameter type
471          * @param distribution ContinuousDistDoubleScalar.Rel&lt;T,U&gt;; the distribution of the parameter
472          */
473         DistributedEntry(final ParameterType<T> parameterType, final ContinuousDistDoubleScalar.Rel<T, U> distribution)
474         {
475             this.parameterType = parameterType;
476             this.distribution = distribution;
477         }
478 
479         /** {@inheritDoc} */
480         @Override
481         public T getValue()
482         {
483             return this.distribution.draw();
484         }
485 
486         /** {@inheritDoc} */
487         @Override
488         public ParameterType<T> getParameterType()
489         {
490             return this.parameterType;
491         }
492 
493         /** {@inheritDoc} */
494         @Override
495         public String toString()
496         {
497             return "DistributedEntry [parameterType=" + this.parameterType + ", distribution=" + this.distribution + "]";
498         }
499     }
500 
501     /**
502      * Distributed double value.
503      * <p>
504      * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
505      * <br>
506      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
507      * <p>
508      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 30 nov. 2016 <br>
509      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
510      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
511      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
512      */
513     private final class DistributedEntryDouble implements ParameterEntry<Double>, Serializable
514     {
515         /** */
516         private static final long serialVersionUID = 20180203L;
517 
518         /** Parameter type. */
519         private final ParameterType<Double> parameterType;
520 
521         /** Parameter distribution. */
522         private final DistContinuous distribution;
523 
524         /**
525          * @param parameterType ParameterTypeDouble; the parameter type
526          * @param distribution DistContinuous; parameter distribution
527          */
528         DistributedEntryDouble(final ParameterType<Double> parameterType, final DistContinuous distribution)
529         {
530             this.parameterType = parameterType;
531             this.distribution = distribution;
532         }
533 
534         /** {@inheritDoc} */
535         @Override
536         public Double getValue()
537         {
538             return this.distribution.draw();
539         }
540 
541         /** {@inheritDoc} */
542         @Override
543         public ParameterType<Double> getParameterType()
544         {
545             return this.parameterType;
546         }
547 
548         /** {@inheritDoc} */
549         @Override
550         public String toString()
551         {
552             return "DistributedEntryDouble [parameterType=" + this.parameterType + ", distribution=" + this.distribution + "]";
553         }
554     }
555 
556     /**
557      * Distributed integer value.
558      * <p>
559      * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
560      * <br>
561      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
562      * <p>
563      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 30 nov. 2016 <br>
564      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
565      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
566      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
567      */
568     private final class DistributedEntryInteger implements ParameterEntry<Integer>, Serializable
569     {
570         /** */
571         private static final long serialVersionUID = 20180203L;
572 
573         /** Parameter type. */
574         private final ParameterType<Integer> parameterType;
575 
576         /** Parameter distribution. */
577         private final DistDiscrete distribution;
578 
579         /**
580          * @param parameterType ParameterTypeInteger; the parameter type
581          * @param distribution DistDiscrete; parameter distribution
582          */
583         DistributedEntryInteger(final ParameterType<Integer> parameterType, final DistDiscrete distribution)
584         {
585             this.parameterType = parameterType;
586             this.distribution = distribution;
587         }
588 
589         /** {@inheritDoc} */
590         @Override
591         public Integer getValue()
592         {
593             return (int) this.distribution.draw();
594         }
595 
596         /** {@inheritDoc} */
597         @Override
598         public ParameterType<Integer> getParameterType()
599         {
600             return this.parameterType;
601         }
602 
603         /** {@inheritDoc} */
604         @Override
605         public String toString()
606         {
607             return "DistributedEntryInteger [parameterType=" + this.parameterType + ", distribution=" + this.distribution + "]";
608         }
609     }
610 
611     /**
612      * Correlates two parameter values.
613      * <p>
614      * Copyright (c) 2013-2019 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
615      * <br>
616      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
617      * <p>
618      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 3 mrt. 2018 <br>
619      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
620      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
621      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
622      * @param <C> value type of independent parameter
623      * @param <T> value type of dependent parameter
624      */
625     @FunctionalInterface
626     public interface Correlation<C, T>
627     {
628         /**
629          * Returns the correlated value.
630          * @param first C; value of independent parameter
631          * @param then T; pre-determined value, the correlation may be relative to a base value
632          * @return correlated value
633          */
634         T correlate(C first, T then);
635     }
636 
637 }