public IEnumerable <string[]> Run(Monitor monitor)
        {
            int               cols = monitor.DataColumns;
            TimeSpan          inactivityThreshold = TimeSpan.FromMinutes(Cfg.SleepIndicatorDurationMin);
            List <MonitorRow> data = monitor.Data
                                     .Where(w => w.Id >= Cfg.FirstIdToAnalyze)
                                     .ToList();

            // 1. compute all relevant cycles
            DroSleepCycle[][] cycles = new DroSleepCycle[cols][];
            for (int col = 0; col < cols; col++)
            {
                cycles[col] = DroSleepCycle.Enumerate(data, col, inactivityThreshold).ToArray();
            }

            // 2. group by interval and compute output
            TimeSpan intervalLength = Cfg.AnalysisIntervalHours > 0
                ? TimeSpan.FromHours(Cfg.AnalysisIntervalHours)
                : TimeSpan.MaxValue;

            IEnumerable <GenericInterval> intervals = GenericInterval
                                                      .EnumerateIntervals(data, intervalLength);

            foreach (GenericInterval interval in intervals)
            {
                yield return(this.CreateRow(cycles, interval, monitor.LightIntervals).ToArray());
            }
        }
        private IEnumerable <string> CreateRow(DroSleepCycle[][] cycles, GenericInterval interval, List <LightInterval> lightIntervals)
        {
            yield return(interval.StartId.ToString());

            yield return(interval.EndId.ToString());

            Func <DroSleepCycle, double> sleepSelector = s =>
            {
                // Charlotte: don't count partial intervals, if cross-border sleep less than threshold
                double partialSleep = s.SleepDurationPartial(interval).TotalMinutes;
                return(partialSleep >= Program.Config.SleepIndicatorDurationMin
                    ? partialSleep
                    : 0);
            };

            Func <DroSleepCycle, bool> lightOnFilter;

            if (this.LightOn.HasValue)
            {
                bool lightOnValue = this.LightOn.Value;
                lightOnFilter = cycle => cycle.SleepStart.IsLightOn == lightOnValue;
            }
            else // use all
            {
                lightOnFilter = cycle => true;
            }


            for (int col = 0; col < cycles.Length; col++)
            {
                DroSleepCycle[] items = cycles[col]
                                        .Where(w => interval.IntersectsSleep(w))
                                        .Where(w => sleepSelector(w) > 0)
                                        .Where(lightOnFilter)
                                        .ToArray();
                switch (this.Mode)
                {
                case AnalysisMode.SleepBoutCount:
                    yield return(items.Length.ToString());

                    break;

                case AnalysisMode.TotalSleep:
                    yield return(items.Sum(sleepSelector).ToString());

                    break;

                case AnalysisMode.SleepBoutDurationAvg:
                    yield return(items.Length > 0
                            ? items.Average(sleepSelector).ToString("F2")
                            : "N/A");

                    break;

                case AnalysisMode.SleepBoutDurationMedian:
                    yield return(items.Length > 0
                            ? items.Median(sleepSelector).ToString()
                            : "N/A");

                    break;

                default:
                    throw new ArgumentOutOfRangeException(this.Mode.ToString());
                }
            }
        }