public bool ProcessSegments()
        {
            bool processedSegments = false;
            bool isRuleEnabled = this.FireRuleProcessing( RulesEngineUtils.SEGMENTS_RULE );
            if( isRuleEnabled )
            {
                BooleanWrapper processed = new BooleanWrapper( false );

                Dictionary<string, object> contextObjects = new Dictionary<string, object>();
                contextObjects.Add( "InstanceReport", this );
                contextObjects.Add( "Processed", processed );

                builderRules.ProcessRule( RulesEngineUtils.SEGMENTS_RULE, contextObjects );

                processedSegments = processed.Value;

                foreach( InstanceReportRow irr in this.Rows )
                {
                    if( irr.IsSegmentTitle )
                    {
                        irr.Label = Regex.Replace( irr.Label, @"\{.*?\} : ", String.Empty ).Trim();
                    }
                }

                this.FireRuleProcessed( RulesEngineUtils.SEGMENTS_RULE );
            }
            return processedSegments;
        }
        public bool ProcessSegments_Rule( BooleanWrapper processed )
        {
            //collect all of the segments to be permuted vertically
            List<InstanceReportColumn> uniqueSegmentColumns = this.GetSegmentScenarioColumnsForSegmentProcessing();
            //if( uniqueSegmentColumns.Count < 2 )
            //	return false;

            //collect all of the calendars to be retained horizontally
            Dictionary<int, List<InstanceReportColumn>> calendarsBySegment = this.GetSegmentScenarioCalendars( uniqueSegmentColumns );
            //if( uniqueSegmentColumns.Count < 2 )
            //	return false;

            //find the set of `allCalendars` which will hold all data without any adjustment
            List<InstanceReportColumn> allCalendars;
            bool doCalendarsOverlap = DoCalendarsOverlap( calendarsBySegment, out allCalendars );
            if( !doCalendarsOverlap )
                return false;

            List<Segment> commonSegmentsToPromote = GetCommonSegments( uniqueSegmentColumns );
            PromoteConsolidatedSegment( uniqueSegmentColumns, commonSegmentsToPromote );

            //set up a temporary holder for the rows - this helps with debugging
            InstanceReport segmentCandidate = new InstanceReport();
            segmentCandidate.Columns.AddRange( allCalendars );

            //The first row in every report is usually `IsReportTitle` - copy it to the temporary report
            InstanceReportRow reportTitleRow = this.FindCloneAndTruncate( row => row.IsReportTitle, allCalendars.Count );
            if( reportTitleRow != null )
            {
                reportTitleRow.Id = 0;
                segmentCandidate.Rows.Add( reportTitleRow );
            }

            //Now, for every `segmentSet`, rebuild the data vertically
            foreach( InstanceReportColumn segmentSet in uniqueSegmentColumns )
            {
                //If this segment set has segments, create an `IsSegmentTitle` row
                if( segmentSet.Segments != null && segmentSet.Segments.Count > 0 )
                {
                    string label = string.Empty;
                    foreach( Segment seg in segmentSet.Segments )
                    {
                        if( commonSegmentsToPromote != null && commonSegmentsToPromote.Count > 0 )
                        {
                            bool isCommon = ReportUtils.Exists( commonSegmentsToPromote, cSeg => cSeg.Equals( seg ) );
                            if( isCommon )
                                continue;
                        }

                        if( !string.IsNullOrEmpty( label ) )
                            label += " | ";

                        label += seg.ValueName;
                    }

                    if( !string.IsNullOrEmpty( label ) )
                    {
                        InstanceReportRow segmentRow = new InstanceReportRow( label, allCalendars.Count );
                        segmentRow.IsSegmentTitle = true;
                        segmentRow.Id = segmentCandidate.Rows.Count;
                        segmentRow.OriginalInstanceReportColumn = (InstanceReportColumn)segmentSet.Clone();

                        segmentCandidate.Rows.Add( segmentRow );
                    }
                }

                //`segmentSets` combined with `rows` provide our vertical (y) axis
                //for each row in the "base" report, we need to pull in the data
                InstanceReportRow lastAbstract = null;
                foreach( InstanceReportRow row in this.Rows )
                {
                    if( row.IsReportTitle )
                        continue;

                    //retain abstracts...
                    if( row.IsAbstractGroupTitle )
                    {
                        //...unless they're consecutive - retain the most recent one
                        if( lastAbstract != null )
                        {
                            int at = segmentCandidate.Rows.IndexOf( lastAbstract );
                            if( at == segmentCandidate.Rows.Count - 1 )
                                segmentCandidate.Rows.RemoveAt( at );
                        }

                        InstanceReportRow abstractRow = CloneAndTruncate( row, allCalendars.Count );
                        abstractRow.Id = segmentCandidate.Rows.Count;
                        segmentCandidate.Rows.Add( abstractRow );

                        lastAbstract = abstractRow;
                        continue;
                    }

                    //`calendars` provide our horizontal (x) axis
                    //`calendars` (x) combined with `segmentSets` & `rows` (y) allow us to look up the correct cell
                    bool hasData = false;
                    InstanceReportRow currentRow = new InstanceReportRow();
                    foreach( InstanceReportColumn calendar in allCalendars )
                    {
                        List<InstanceReportColumn> matches = this.GetMatchingColumns( calendar, segmentSet, row );

                        //apply exact match
                        InstanceReportColumn exactColumn = matches.Find( m => m.ReportingPeriodEquals( calendar ) );
                        if( exactColumn != null )
                        {
                            Cell exactCell = row.Cells.Find( c => c.Id == exactColumn.Id );
                            if( exactCell != null && exactCell.HasData )
                            {
                                hasData = true;
                                Cell newCell = (Cell)exactCell.Clone();
                                newCell.EmbeddedReport = exactCell.EmbeddedReport;

                                if( !string.IsNullOrEmpty( segmentSet.CurrencyCode ) )
                                {
                                    if( (int)row.Unit == (int)UnitType.Monetary ||
                                        (int)row.Unit == (int)UnitType.EPS )
                                    {
                                        newCell.CurrencyCode = segmentSet.CurrencyCode;
                                        newCell.CurrencySymbol = segmentSet.CurrencySymbol;
                                    }
                                }

                                currentRow.Cells.Add( newCell );
                                continue;
                            }
                        }

                        //apply similar matches
                        {
                            List<Cell> cells = matches.ConvertAll( col => row.Cells.Find( c => c.Id == col.Id ) );

                            //Now reduce our cells to those with values...
                            cells.RemoveAll( c => c == null || !c.HasData );

                            //...and non-duplicates
                            for( int c = 0; c < cells.Count; c++ )
                            {
                                Cell curCell = cells[ c ];
                                cells.RemoveAll( cell => cell.Id != curCell.Id && cell.NumericAmount == curCell.NumericAmount );
                            }

                            switch( cells.Count )
                            {
                                case 0:
                                    Cell emptyCell = new Cell();
                                    currentRow.Cells.Add( emptyCell );
                                    break;
                                case 1:
                                    hasData = true;
                                    Cell newCell = (Cell)cells[ 0 ].Clone();
                                    newCell.EmbeddedReport = cells[ 0 ].EmbeddedReport;

                                    if( !string.IsNullOrEmpty( segmentSet.CurrencyCode ) )
                                    {
                                        if( (int)row.Unit == (int)UnitType.Monetary ||
                                            (int)row.Unit == (int)UnitType.EPS )
                                        {
                                            newCell.CurrencyCode = segmentSet.CurrencyCode;
                                            newCell.CurrencySymbol = segmentSet.CurrencySymbol;
                                        }
                                    }

                                    currentRow.Cells.Add( newCell );
                                    break;
                                default:
                                    Debug.Assert( false, "Too many cells" );
                                    break;
                            }
                        }
                    }

                    //if we actually found data for this row, let's clone the original, and swap out the cells
                    if( hasData )
                    {
                        InstanceReportRow clonedRow = (InstanceReportRow)row.Clone( false, false );
                        clonedRow.Cells.AddRange( currentRow.Cells );
                        clonedRow.Id = segmentCandidate.Rows.Count;

                        segmentCandidate.Rows.Add( clonedRow );
                    }
                }

                //Same as above, don't preserve consecutive abstract rows
                if( lastAbstract != null )
                {
                    int at = segmentCandidate.Rows.IndexOf( lastAbstract );
                    if( at == segmentCandidate.Rows.Count - 1 )
                        segmentCandidate.Rows.RemoveAt( at );
                }
            }

            //now that the permutation is complete, apply the new rows and columns to the "base" report
            this.Columns.Clear();
            this.Columns.AddRange( segmentCandidate.Columns );

            this.Rows.Clear();
            this.Rows.AddRange( segmentCandidate.Rows );

            this.SynchronizeGrid();

            //this.InstantValues();

            processed.Value = true;
            return true;
        }