anchor.diff:      along with this program.  If not, see <http://www.gnu.org/licenses/>.
   *)
   
  -#nowarn "0086" // operators overloads are intential, Series are as primitive as scalars, all arithmetic operations are defined on them as maps
  +#nowarn "0086" // operator overloads are intentional, Series are as primitive as scalars, all arithmetic operations are defined on them as maps
   namespace Spreads
   
   open System
  
positive.code: //              OptionalValue(Func<_,_>(fBatch'))
  //            else None
          CursorSeries(fun _ -> new BatchMapValuesCursor<'K,'V,'V2>(Func<_>(source.GetCursor), mapF, fBatch) :> ICursor<_,_>) :> Series<'K,'V2>
  #if PRERELEASE // we could switch off this optimization in prerelease builds
        if OptimizationSettings.CombineFilterMapDelegates then
          match box source with
          | :? ICanMapSeriesValues<'K,'V> as s -> s.Map(mapFunc)
          | _ ->  defaultMap()
        else defaultMap()
  #else
        defaultMap()
  #endif
  
  
      // TODO! (perf) optimize ZipN for 2, or reimplement Zip for 'V/'V2->'R, see commented out cursor below
  
negative.code:     let continuous = cursors |> Array.map (fun x -> x.IsContinuous) 
      let isContinuous = continuous |> Array.forall id
  
      // The state is valid when
      // For discrete case - all discrete cursors move at some location (first/last/at)
      // For continuous case - at least one continuous series moved at some location
      // and we could call do... methods after that
      let mutable hasValidState = false
      
      // all keys where discrete cursors are positioned. they define where resulting keys are present
      let discreteKeysSet = SortedDeque(ZipNComparer(cmp))
      // active continuous cursors
  