| using J2N.Numerics; |
| using Lucene.Net.Diagnostics; |
| using System; |
| using System.Globalization; |
| |
| namespace Lucene.Net.Util.Packed |
| { |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /// <summary> |
| /// A decoder for an <see cref="Packed.EliasFanoEncoder"/>. |
| /// <para/> |
| /// @lucene.internal |
| /// </summary> |
| public class EliasFanoDecoder |
| { |
| /// <summary> |
| /// NOTE: This was LOG2_LONG_SIZE in Lucene. |
| /// </summary> |
| private static readonly int LOG2_INT64_SIZE = (sizeof(long) * 8).TrailingZeroCount(); |
| |
| private readonly EliasFanoEncoder efEncoder; |
| private readonly long numEncoded; |
| private long efIndex = -1; // the decoding index. |
| private long setBitForIndex = -1; // the index of the high bit at the decoding index. |
| |
| public const long NO_MORE_VALUES = -1L; |
| |
| private readonly long numIndexEntries; |
| private readonly long indexMask; |
| |
| /// <summary> |
| /// Construct a decoder for a given <see cref="Packed.EliasFanoEncoder"/>. |
| /// The decoding index is set to just before the first encoded value. |
| /// </summary> |
| public EliasFanoDecoder(EliasFanoEncoder efEncoder) |
| { |
| this.efEncoder = efEncoder; |
| this.numEncoded = efEncoder.numEncoded; // not final in EliasFanoEncoder |
| this.numIndexEntries = efEncoder.currentEntryIndex; // not final in EliasFanoEncoder |
| this.indexMask = (1L << efEncoder.nIndexEntryBits) - 1; |
| } |
| |
| /// <returns> The Elias-Fano encoder that is decoded. </returns> |
| public virtual EliasFanoEncoder EliasFanoEncoder => efEncoder; |
| |
| /// <summary> |
| /// The number of values encoded by the encoder. </summary> |
| /// <returns> The number of values encoded by the encoder. </returns> |
| public virtual long NumEncoded => numEncoded; |
| |
| /// <summary> |
| /// The current decoding index. |
| /// The first value encoded by <see cref="EliasFanoEncoder.EncodeNext(long)"/> has index 0. |
| /// Only valid directly after |
| /// <see cref="NextValue()"/>, <see cref="AdvanceToValue(long)"/>, |
| /// <see cref="PreviousValue()"/>, or <see cref="BackToValue(long)"/> |
| /// returned another value than <see cref="NO_MORE_VALUES"/>, |
| /// or <see cref="AdvanceToIndex(long)"/> returned <c>true</c>. </summary> |
| /// <returns> The decoding index of the last decoded value, or as last set by <see cref="AdvanceToIndex(long)"/>. </returns> |
| public virtual long CurrentIndex() |
| { |
| if (efIndex < 0) |
| { |
| throw new InvalidOperationException("index before sequence"); |
| } |
| if (efIndex >= numEncoded) |
| { |
| throw new InvalidOperationException("index after sequence"); |
| } |
| return efIndex; |
| } |
| |
| /// <summary> |
| /// The value at the current decoding index. |
| /// Only valid when <see cref="CurrentIndex()"/> would return a valid result. |
| /// <para/> |
| /// This is only intended for use after <see cref="AdvanceToIndex(long)"/> returned <c>true</c>. </summary> |
| /// <returns> The value encoded at <see cref="CurrentIndex()"/>. </returns> |
| public virtual long CurrentValue() |
| { |
| return CombineHighLowValues(CurrentHighValue(), CurrentLowValue()); |
| } |
| |
| /// <returns> The high value for the current decoding index. </returns> |
| private long CurrentHighValue() |
| { |
| return setBitForIndex - efIndex; // sequence of unary gaps |
| } |
| |
| /// <summary> |
| /// See also <see cref="EliasFanoEncoder.PackValue(long, long[], int, long)"/> </summary> |
| private static long UnPackValue(long[] longArray, int numBits, long packIndex, long bitsMask) |
| { |
| if (numBits == 0) |
| { |
| return 0; |
| } |
| long bitPos = packIndex * numBits; |
| int index = (int)((long)((ulong)bitPos >> LOG2_INT64_SIZE)); |
| int bitPosAtIndex = (int)(bitPos & ((sizeof(long) * 8) - 1)); |
| long value = (long)((ulong)longArray[index] >> bitPosAtIndex); |
| if ((bitPosAtIndex + numBits) > (sizeof(long) * 8)) |
| { |
| value |= (longArray[index + 1] << ((sizeof(long) * 8) - bitPosAtIndex)); |
| } |
| value &= bitsMask; |
| return value; |
| } |
| |
| /// <returns> The low value for the current decoding index. </returns> |
| private long CurrentLowValue() |
| { |
| if (Debugging.AssertsEnabled) Debugging.Assert(((efIndex >= 0) && (efIndex < numEncoded)), "efIndex {0}", efIndex); |
| return UnPackValue(efEncoder.lowerLongs, efEncoder.numLowBits, efIndex, efEncoder.lowerBitsMask); |
| } |
| |
| /// <returns> The given <paramref name="highValue"/> shifted left by the number of low bits from by the EliasFanoSequence, |
| /// logically OR-ed with the given <paramref name="lowValue"/>. </returns> |
| private long CombineHighLowValues(long highValue, long lowValue) |
| { |
| return (highValue << efEncoder.numLowBits) | lowValue; |
| } |
| |
| private long curHighLong; |
| |
| /* The implementation of forward decoding and backward decoding is done by the following method pairs. |
| * |
| * toBeforeSequence - toAfterSequence |
| * getCurrentRightShift - getCurrentLeftShift |
| * toAfterCurrentHighBit - toBeforeCurrentHighBit |
| * toNextHighLong - toPreviousHighLong |
| * nextHighValue - previousHighValue |
| * nextValue - previousValue |
| * advanceToValue - backToValue |
| * |
| */ |
| |
| /* Forward decoding section */ |
| |
| /// <summary> |
| /// Set the decoding index to just before the first encoded value. |
| /// </summary> |
| public virtual void ToBeforeSequence() |
| { |
| efIndex = -1; |
| setBitForIndex = -1; |
| } |
| |
| /// <returns> The number of bits in a <see cref="long"/> after (<see cref="setBitForIndex"/> modulo <c>sizeof(long)</c>). </returns> |
| private int CurrentRightShift |
| { |
| get |
| { |
| int s = (int)(setBitForIndex & ((sizeof(long) * 8) - 1)); |
| return s; |
| } |
| } |
| |
| /// <summary> |
| /// Increment <see cref="efIndex"/> and <see cref="setBitForIndex"/> and |
| /// shift <see cref="curHighLong"/> so that it does not contain the high bits before <see cref="setBitForIndex"/>. </summary> |
| /// <returns> <c>true</c> if <see cref="efIndex"/> still smaller than <see cref="numEncoded"/>. </returns> |
| private bool ToAfterCurrentHighBit() |
| { |
| efIndex += 1; |
| if (efIndex >= numEncoded) |
| { |
| return false; |
| } |
| setBitForIndex += 1; |
| int highIndex = (int)((long)((ulong)setBitForIndex >> LOG2_INT64_SIZE)); |
| curHighLong = (long)((ulong)efEncoder.upperLongs[highIndex] >> CurrentRightShift); |
| return true; |
| } |
| |
| /// <summary> |
| /// The current high long has been determined to not contain the set bit that is needed. |
| /// Increment <see cref="setBitForIndex"/> to the next high long and set <see cref="curHighLong"/> accordingly. |
| /// <para/> |
| /// NOTE: this was toNextHighLong() in Lucene. |
| /// </summary> |
| private void ToNextHighInt64() |
| { |
| setBitForIndex += (sizeof(long) * 8) - (setBitForIndex & ((sizeof(long) * 8) - 1)); |
| //assert getCurrentRightShift() == 0; |
| int highIndex = (int)((long)((ulong)setBitForIndex >> LOG2_INT64_SIZE)); |
| curHighLong = efEncoder.upperLongs[highIndex]; |
| } |
| |
| /// <summary> |
| /// <see cref="setBitForIndex"/> and <see cref="efIndex"/> have just been incremented, scan to the next high set bit |
| /// by incrementing <see cref="setBitForIndex"/>, and by setting <see cref="curHighLong"/> accordingly. |
| /// </summary> |
| private void ToNextHighValue() |
| { |
| while (curHighLong == 0L) |
| { |
| ToNextHighInt64(); // inlining and unrolling would simplify somewhat |
| } |
| setBitForIndex += curHighLong.TrailingZeroCount(); |
| } |
| |
| /// <summary> |
| /// <see cref="setBitForIndex"/> and <see cref="efIndex"/> have just been incremented, scan to the next high set bit |
| /// by incrementing <see cref="setBitForIndex"/>, and by setting <see cref="curHighLong"/> accordingly. </summary> |
| /// <returns> The next encoded high value. </returns> |
| private long NextHighValue() |
| { |
| ToNextHighValue(); |
| return CurrentHighValue(); |
| } |
| |
| /// <summary> |
| /// If another value is available after the current decoding index, return this value and |
| /// and increase the decoding index by 1. Otherwise return <see cref="NO_MORE_VALUES"/>. |
| /// </summary> |
| public virtual long NextValue() |
| { |
| if (!ToAfterCurrentHighBit()) |
| { |
| return NO_MORE_VALUES; |
| } |
| long highValue = NextHighValue(); |
| return CombineHighLowValues(highValue, CurrentLowValue()); |
| } |
| |
| /// <summary> |
| /// Advance the decoding index to a given <paramref name="index"/>. |
| /// and return <c>true</c> iff it is available. |
| /// <para/>See also <see cref="CurrentValue()"/>. |
| /// <para/>The current implementation does not use the index on the upper bit zero bit positions. |
| /// <para/>Note: there is currently no implementation of <c>BackToIndex()</c>. |
| /// </summary> |
| public virtual bool AdvanceToIndex(long index) |
| { |
| if (Debugging.AssertsEnabled) Debugging.Assert(index > efIndex); |
| if (index >= numEncoded) |
| { |
| efIndex = numEncoded; |
| return false; |
| } |
| if (!ToAfterCurrentHighBit()) |
| { |
| if (Debugging.AssertsEnabled) Debugging.Assert(false); |
| } |
| /* CHECKME: Add a (binary) search in the upperZeroBitPositions here. */ |
| int curSetBits = curHighLong.PopCount(); |
| while ((efIndex + curSetBits) < index) // curHighLong has not enough set bits to reach index |
| { |
| efIndex += curSetBits; |
| ToNextHighInt64(); |
| curSetBits = curHighLong.PopCount(); |
| } |
| // curHighLong has enough set bits to reach index |
| while (efIndex < index) |
| { |
| /* CHECKME: Instead of the linear search here, use (forward) broadword selection from |
| * "Broadword Implementation of Rank/Select Queries", Sebastiano Vigna, January 30, 2012. |
| */ |
| if (!ToAfterCurrentHighBit()) |
| { |
| if (Debugging.AssertsEnabled) Debugging.Assert(false); |
| } |
| ToNextHighValue(); |
| } |
| return true; |
| } |
| |
| /// <summary> |
| /// Given a <paramref name="target"/> value, advance the decoding index to the first bigger or equal value |
| /// and return it if it is available. Otherwise return <see cref="NO_MORE_VALUES"/>. |
| /// <para/> |
| /// The current implementation uses the index on the upper zero bit positions. |
| /// </summary> |
| public virtual long AdvanceToValue(long target) |
| { |
| efIndex += 1; |
| if (efIndex >= numEncoded) |
| { |
| return NO_MORE_VALUES; |
| } |
| setBitForIndex += 1; // the high bit at setBitForIndex belongs to the unary code for efIndex |
| |
| int highIndex = (int)((long)((ulong)setBitForIndex >> LOG2_INT64_SIZE)); |
| long upperLong = efEncoder.upperLongs[highIndex]; |
| curHighLong = (long)((ulong)upperLong >> ((int)(setBitForIndex & ((sizeof(long) * 8) - 1)))); // may contain the unary 1 bit for efIndex |
| |
| // determine index entry to advance to |
| long highTarget = (long)((ulong)target >> efEncoder.numLowBits); |
| |
| long indexEntryIndex = (highTarget / efEncoder.indexInterval) - 1; |
| if (indexEntryIndex >= 0) // not before first index entry |
| { |
| if (indexEntryIndex >= numIndexEntries) |
| { |
| indexEntryIndex = numIndexEntries - 1; // no further than last index entry |
| } |
| long indexHighValue = (indexEntryIndex + 1) * efEncoder.indexInterval; |
| if (Debugging.AssertsEnabled) Debugging.Assert(indexHighValue <= highTarget); |
| if (indexHighValue > (setBitForIndex - efIndex)) // advance to just after zero bit position of index entry. |
| { |
| setBitForIndex = UnPackValue(efEncoder.upperZeroBitPositionIndex, efEncoder.nIndexEntryBits, indexEntryIndex, indexMask); |
| efIndex = setBitForIndex - indexHighValue; // the high bit at setBitForIndex belongs to the unary code for efIndex |
| highIndex = (int)(((ulong)setBitForIndex >> LOG2_INT64_SIZE)); |
| upperLong = efEncoder.upperLongs[highIndex]; |
| curHighLong = (long)((ulong)upperLong >> ((int)(setBitForIndex & ((sizeof(long) * 8) - 1)))); // may contain the unary 1 bit for efIndex |
| } |
| if (Debugging.AssertsEnabled) Debugging.Assert(efIndex < numEncoded); // there is a high value to be found. |
| } |
| |
| int curSetBits = curHighLong.PopCount(); // shifted right. |
| int curClearBits = (sizeof(long) * 8) - curSetBits - ((int)(setBitForIndex & ((sizeof(long) * 8) - 1))); // subtract right shift, may be more than encoded |
| |
| while (((setBitForIndex - efIndex) + curClearBits) < highTarget) |
| { |
| // curHighLong has not enough clear bits to reach highTarget |
| efIndex += curSetBits; |
| if (efIndex >= numEncoded) |
| { |
| return NO_MORE_VALUES; |
| } |
| setBitForIndex += (sizeof(long) * 8) - (setBitForIndex & ((sizeof(long) * 8) - 1)); |
| // highIndex = (int)(setBitForIndex >>> LOG2_LONG_SIZE); |
| if (Debugging.AssertsEnabled) Debugging.Assert((highIndex + 1) == (int)((long)((ulong)setBitForIndex >> LOG2_INT64_SIZE))); |
| highIndex += 1; |
| upperLong = efEncoder.upperLongs[highIndex]; |
| curHighLong = upperLong; |
| curSetBits = curHighLong.PopCount(); |
| curClearBits = (sizeof(long) * 8) - curSetBits; |
| } |
| // curHighLong has enough clear bits to reach highTarget, and may not have enough set bits. |
| while (curHighLong == 0L) |
| { |
| setBitForIndex += (sizeof(long) * 8) - (setBitForIndex & ((sizeof(long) * 8) - 1)); |
| if (Debugging.AssertsEnabled) Debugging.Assert((highIndex + 1) == (int)((ulong)setBitForIndex >> LOG2_INT64_SIZE)); |
| highIndex += 1; |
| upperLong = efEncoder.upperLongs[highIndex]; |
| curHighLong = upperLong; |
| } |
| |
| // curHighLong has enough clear bits to reach highTarget, has at least 1 set bit, and may not have enough set bits. |
| int rank = (int)(highTarget - (setBitForIndex - efIndex)); // the rank of the zero bit for highValue. |
| if (Debugging.AssertsEnabled) Debugging.Assert((rank <= (sizeof(long) * 8)), "rank {0}", rank); |
| if (rank >= 1) |
| { |
| long invCurHighLong = ~curHighLong; |
| int clearBitForValue = (rank <= 8) ? BroadWord.SelectNaive(invCurHighLong, rank) : BroadWord.Select(invCurHighLong, rank); |
| if (Debugging.AssertsEnabled) Debugging.Assert(clearBitForValue <= ((sizeof(long) * 8) - 1)); |
| setBitForIndex += clearBitForValue + 1; // the high bit just before setBitForIndex is zero |
| int oneBitsBeforeClearBit = clearBitForValue - rank + 1; |
| efIndex += oneBitsBeforeClearBit; // the high bit at setBitForIndex and belongs to the unary code for efIndex |
| if (efIndex >= numEncoded) |
| { |
| return NO_MORE_VALUES; |
| } |
| |
| if ((setBitForIndex & ((sizeof(long) * 8) - 1)) == 0L) // exhausted curHighLong |
| { |
| if (Debugging.AssertsEnabled) Debugging.Assert((highIndex + 1) == (int)((ulong)setBitForIndex >> LOG2_INT64_SIZE)); |
| highIndex += 1; |
| upperLong = efEncoder.upperLongs[highIndex]; |
| curHighLong = upperLong; |
| } |
| else |
| { |
| if (Debugging.AssertsEnabled) Debugging.Assert(highIndex == (int)((ulong)setBitForIndex >> LOG2_INT64_SIZE)); |
| curHighLong = (long)((ulong)upperLong >> ((int)(setBitForIndex & ((sizeof(long) * 8) - 1)))); |
| } |
| // curHighLong has enough clear bits to reach highTarget, and may not have enough set bits. |
| |
| while (curHighLong == 0L) |
| { |
| setBitForIndex += (sizeof(long) * 8) - (setBitForIndex & ((sizeof(long) * 8) - 1)); |
| if (Debugging.AssertsEnabled) Debugging.Assert((highIndex + 1) == (int)((ulong)setBitForIndex >> LOG2_INT64_SIZE)); |
| highIndex += 1; |
| upperLong = efEncoder.upperLongs[highIndex]; |
| curHighLong = upperLong; |
| } |
| } |
| setBitForIndex += curHighLong.TrailingZeroCount(); |
| if (Debugging.AssertsEnabled) Debugging.Assert((setBitForIndex - efIndex) >= highTarget); // highTarget reached |
| |
| // Linear search also with low values |
| long currentValue = CombineHighLowValues((setBitForIndex - efIndex), CurrentLowValue()); |
| while (currentValue < target) |
| { |
| currentValue = NextValue(); |
| if (currentValue == NO_MORE_VALUES) |
| { |
| return NO_MORE_VALUES; |
| } |
| } |
| return currentValue; |
| } |
| |
| /* Backward decoding section */ |
| |
| /// <summary> |
| /// Set the decoding index to just after the last encoded value. |
| /// </summary> |
| public virtual void ToAfterSequence() |
| { |
| efIndex = numEncoded; // just after last index |
| setBitForIndex = ((long)((ulong)efEncoder.lastEncoded >> efEncoder.numLowBits)) + numEncoded; |
| } |
| |
| /// <returns> the number of bits in a long before (<see cref="setBitForIndex"/> modulo <c>sizeof(long)</c>) </returns> |
| private int CurrentLeftShift |
| { |
| get |
| { |
| int s = (sizeof(long) * 8) - 1 - (int)(setBitForIndex & ((sizeof(long) * 8) - 1)); |
| return s; |
| } |
| } |
| |
| /// <summary> |
| /// Decrement <see cref="efIndex"/> and <see cref="setBitForIndex"/> and |
| /// shift <see cref="curHighLong"/> so that it does not contain the high bits after <see cref="setBitForIndex"/>. </summary> |
| /// <returns> <c>true</c> if <see cref="efIndex"/> still >= 0. </returns> |
| private bool ToBeforeCurrentHighBit() |
| { |
| efIndex -= 1; |
| if (efIndex < 0) |
| { |
| return false; |
| } |
| setBitForIndex -= 1; |
| int highIndex = (int)((ulong)setBitForIndex >> LOG2_INT64_SIZE); |
| curHighLong = efEncoder.upperLongs[highIndex] << CurrentLeftShift; |
| return true; |
| } |
| |
| /// <summary> |
| /// The current high long has been determined to not contain the set bit that is needed. |
| /// Decrement <see cref="setBitForIndex"/> to the previous high long and set <see cref="curHighLong"/> accordingly. |
| /// <para/> |
| /// NOTE: this was toPreviousHighLong() in Lucene. |
| /// </summary> |
| private void ToPreviousHighInt64() |
| { |
| setBitForIndex -= (setBitForIndex & ((sizeof(long) * 8) - 1)) + 1; |
| //assert getCurrentLeftShift() == 0; |
| int highIndex = (int)((ulong)setBitForIndex >> LOG2_INT64_SIZE); |
| curHighLong = efEncoder.upperLongs[highIndex]; |
| } |
| |
| /// <summary> |
| /// <see cref="setBitForIndex"/> and <see cref="efIndex"/> have just been decremented, scan to the previous high set bit |
| /// by decrementing <see cref="setBitForIndex"/> and by setting <see cref="curHighLong"/> accordingly. </summary> |
| /// <returns> The previous encoded high value. </returns> |
| private long PreviousHighValue() |
| { |
| while (curHighLong == 0L) |
| { |
| ToPreviousHighInt64(); // inlining and unrolling would simplify somewhat |
| } |
| setBitForIndex -= curHighLong.LeadingZeroCount(); |
| return CurrentHighValue(); |
| } |
| |
| /// <summary> |
| /// If another value is available before the current decoding index, return this value |
| /// and decrease the decoding index by 1. Otherwise return <see cref="NO_MORE_VALUES"/>. |
| /// </summary> |
| public virtual long PreviousValue() |
| { |
| if (!ToBeforeCurrentHighBit()) |
| { |
| return NO_MORE_VALUES; |
| } |
| long highValue = PreviousHighValue(); |
| return CombineHighLowValues(highValue, CurrentLowValue()); |
| } |
| |
| /// <summary> |
| /// <see cref="setBitForIndex"/> and <see cref="efIndex"/> have just been decremented, scan backward to the high set bit |
| /// of at most a given high value |
| /// by decrementing <see cref="setBitForIndex"/> and by setting <see cref="curHighLong"/> accordingly. |
| /// <para/> |
| /// The current implementation does not use the index on the upper zero bit positions. |
| /// </summary> |
| /// <returns> The largest encoded high value that is at most the given one. </returns> |
| private long BackToHighValue(long highTarget) |
| { |
| /* CHECKME: Add using the index as in advanceToHighValue */ |
| int curSetBits = curHighLong.PopCount(); // is shifted by getCurrentLeftShift() |
| int curClearBits = (sizeof(long) * 8) - curSetBits - CurrentLeftShift; |
| while ((CurrentHighValue() - curClearBits) > highTarget) |
| { |
| // curHighLong has not enough clear bits to reach highTarget |
| efIndex -= curSetBits; |
| if (efIndex < 0) |
| { |
| return NO_MORE_VALUES; |
| } |
| ToPreviousHighInt64(); |
| //assert getCurrentLeftShift() == 0; |
| curSetBits = curHighLong.PopCount(); |
| curClearBits = (sizeof(long) * 8) - curSetBits; |
| } |
| // curHighLong has enough clear bits to reach highTarget, but may not have enough set bits. |
| long highValue = PreviousHighValue(); |
| while (highValue > highTarget) |
| { |
| /* CHECKME: See at advanceToHighValue on using broadword bit selection. */ |
| if (!ToBeforeCurrentHighBit()) |
| { |
| return NO_MORE_VALUES; |
| } |
| highValue = PreviousHighValue(); |
| } |
| return highValue; |
| } |
| |
| /// <summary> |
| /// Given a target value, go back to the first smaller or equal value |
| /// and return it if it is available. Otherwise return <see cref="NO_MORE_VALUES"/>. |
| /// <para/> |
| /// The current implementation does not use the index on the upper zero bit positions. |
| /// </summary> |
| public virtual long BackToValue(long target) |
| { |
| if (!ToBeforeCurrentHighBit()) |
| { |
| return NO_MORE_VALUES; |
| } |
| long highTarget = (long)((ulong)target >> efEncoder.numLowBits); |
| long highValue = BackToHighValue(highTarget); |
| if (highValue == NO_MORE_VALUES) |
| { |
| return NO_MORE_VALUES; |
| } |
| // Linear search with low values: |
| long currentValue = CombineHighLowValues(highValue, CurrentLowValue()); |
| while (currentValue > target) |
| { |
| currentValue = PreviousValue(); |
| if (currentValue == NO_MORE_VALUES) |
| { |
| return NO_MORE_VALUES; |
| } |
| } |
| return currentValue; |
| } |
| } |
| } |