// TODO: should we return a status here (SEEK_FOUND / SEEK_NOT_FOUND / // SEEK_END)? saves the eq check above? /// <summary> /// Seeks to largest term that's <= target. </summary> protected virtual void DoSeekFloor() { // TODO: possibly caller could/should provide common // prefix length? ie this work may be redundant if // caller is in fact intersecting against its own // automaton //System.out.println("FE: seek floor upto=" + upto); // Save CPU by starting at the end of the shared prefix // b/w our current term & the target: RewindPrefix(); //System.out.println("FE: after rewind upto=" + upto); FST.Arc <T> arc = GetArc(m_upto); int targetLabel = TargetLabel; //System.out.println("FE: init targetLabel=" + targetLabel); // Now scan forward, matching the new suffix of the target while (true) { //System.out.println(" cycle upto=" + upto + " arc.label=" + arc.label + " (" + (char) arc.label + ") targetLabel=" + targetLabel + " isLast?=" + arc.isLast() + " bba=" + arc.bytesPerArc); if (arc.BytesPerArc != 0 && arc.Label != FST.END_LABEL) { // Arcs are fixed array -- use binary search to find // the target. FST.BytesReader @in = m_fst.GetBytesReader(); int low = arc.ArcIdx; int high = arc.NumArcs - 1; int mid = 0; //System.out.println("do arc array low=" + low + " high=" + high + " targetLabel=" + targetLabel); bool found = false; while (low <= high) { mid = (int)((uint)(low + high) >> 1); @in.Position = arc.PosArcsStart; @in.SkipBytes(arc.BytesPerArc * mid + 1); int midLabel = m_fst.ReadLabel(@in); int cmp = midLabel - targetLabel; //System.out.println(" cycle low=" + low + " high=" + high + " mid=" + mid + " midLabel=" + midLabel + " cmp=" + cmp); if (cmp < 0) { low = mid + 1; } else if (cmp > 0) { high = mid - 1; } else { found = true; break; } } // NOTE: this code is dup'd w/ the code below (in // the outer else clause): if (found) { // Match -- recurse //System.out.println(" match! arcIdx=" + mid); arc.ArcIdx = mid - 1; m_fst.ReadNextRealArc(arc, @in); if (Debugging.AssertsEnabled) { Debugging.Assert(arc.ArcIdx == mid); Debugging.Assert(arc.Label == targetLabel, "arc.label={0} vs targetLabel={1} mid={2}", arc.Label, targetLabel, mid); } m_output[m_upto] = m_fst.Outputs.Add(m_output[m_upto - 1], arc.Output); if (targetLabel == FST.END_LABEL) { return; } CurrentLabel = arc.Label; Incr(); arc = m_fst.ReadFirstTargetArc(arc, GetArc(m_upto), m_fstReader); targetLabel = TargetLabel; continue; } else if (high == -1) { //System.out.println(" before first"); // Very first arc is after our target // TODO: if each arc could somehow read the arc just // before, we can save this re-scan. The ceil case // doesn't need this because it reads the next arc // instead: while (true) { // First, walk backwards until we find a first arc // that's before our target label: m_fst.ReadFirstTargetArc(GetArc(m_upto - 1), arc, m_fstReader); if (arc.Label < targetLabel) { // Then, scan forwards to the arc just before // the targetLabel: while (!arc.IsLast && m_fst.ReadNextArcLabel(arc, @in) < targetLabel) { m_fst.ReadNextArc(arc, m_fstReader); } PushLast(); return; } m_upto--; if (m_upto == 0) { return; } targetLabel = TargetLabel; arc = GetArc(m_upto); } } else { // There is a floor arc: arc.ArcIdx = (low > high ? high : low) - 1; //System.out.println(" hasFloor arcIdx=" + (arc.arcIdx+1)); m_fst.ReadNextRealArc(arc, @in); // LUCNENET specific: We don't want the ReadNextArcLabel call to be // excluded when Debug.Assert is stripped out by the compiler. bool check = arc.IsLast || m_fst.ReadNextArcLabel(arc, @in) > targetLabel; if (Debugging.AssertsEnabled) { Debugging.Assert(check); Debugging.Assert(arc.Label < targetLabel, "arc.label={0} vs targetLabel={1}", arc.Label, targetLabel); } PushLast(); return; } } else { if (arc.Label == targetLabel) { // Match -- recurse m_output[m_upto] = m_fst.Outputs.Add(m_output[m_upto - 1], arc.Output); if (targetLabel == FST.END_LABEL) { return; } CurrentLabel = arc.Label; Incr(); arc = m_fst.ReadFirstTargetArc(arc, GetArc(m_upto), m_fstReader); targetLabel = TargetLabel; } else if (arc.Label > targetLabel) { // TODO: if each arc could somehow read the arc just // before, we can save this re-scan. The ceil case // doesn't need this because it reads the next arc // instead: while (true) { // First, walk backwards until we find a first arc // that's before our target label: m_fst.ReadFirstTargetArc(GetArc(m_upto - 1), arc, m_fstReader); if (arc.Label < targetLabel) { // Then, scan forwards to the arc just before // the targetLabel: while (!arc.IsLast && m_fst.ReadNextArcLabel(arc, m_fstReader) < targetLabel) { m_fst.ReadNextArc(arc, m_fstReader); } PushLast(); return; } m_upto--; if (m_upto == 0) { return; } targetLabel = TargetLabel; arc = GetArc(m_upto); } } else if (!arc.IsLast) { //System.out.println(" check next label=" + fst.readNextArcLabel(arc) + " (" + (char) fst.readNextArcLabel(arc) + ")"); if (m_fst.ReadNextArcLabel(arc, m_fstReader) > targetLabel) { PushLast(); return; } else { // keep scanning m_fst.ReadNextArc(arc, m_fstReader); } } else { PushLast(); return; } } } }