| using Lucene.Net.Diagnostics; |
| using Lucene.Net.Store; |
| using System; |
| using System.Runtime.CompilerServices; |
| |
| namespace Lucene.Net.Util.Fst |
| { |
| /* |
| * 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> |
| /// An FST <see cref="Outputs{T}"/> implementation where each output |
| /// is one or two non-negative long values. If it's a |
| /// <see cref="float"/> output, <see cref="Nullable{Int64}"/> is |
| /// returned; else, <see cref="TwoInt64s"/>. Order |
| /// is preserved in the <see cref="TwoInt64s"/> case, ie .first is the first |
| /// input/output added to <see cref="Builder{T}"/>, and .second is the |
| /// second. You cannot store 0 output with this (that's |
| /// reserved to mean "no output")! |
| /// |
| /// <para>NOTE: the only way to create a TwoLongs output is to |
| /// add the same input to the FST twice in a row. This is |
| /// how the FST maps a single input to two outputs (e.g. you |
| /// cannot pass a <see cref="TwoInt64s"/> to <see cref="Builder{T}.Add(Int32sRef, T)"/>. If you |
| /// need more than two then use <see cref="ListOfOutputs{T}"/>, but if |
| /// you only have at most 2 then this implementation will |
| /// require fewer bytes as it steals one bit from each long |
| /// value. |
| /// |
| /// </para> |
| /// <para>NOTE: the resulting FST is not guaranteed to be minimal! |
| /// See <see cref="Builder{T}"/>. |
| /// </para> |
| /// <para> |
| /// NOTE: This was UpToTwoPositiveIntOutputs in Lucene - the data type (int) was wrong there - it should have been long |
| /// </para> |
| /// @lucene.experimental |
| /// </summary> |
| public sealed class UpToTwoPositiveInt64Outputs : Outputs<object> |
| { |
| /// <summary> |
| /// Holds two long outputs. |
| /// <para/> |
| /// NOTE: This was TwoLongs in Lucene |
| /// </summary> |
| public sealed class TwoInt64s |
| { |
| public long First => first; |
| private readonly long first; |
| |
| public long Second => second; |
| private readonly long second; |
| |
| public TwoInt64s(long first, long second) |
| { |
| this.first = first; |
| this.second = second; |
| if (Debugging.AssertsEnabled) |
| { |
| Debugging.Assert(first >= 0); |
| Debugging.Assert(second >= 0); |
| } |
| } |
| |
| public override string ToString() |
| { |
| return "TwoLongs:" + first + "," + second; |
| } |
| |
| public override bool Equals(object other) |
| { |
| if (other is TwoInt64s other2) |
| { |
| return first == other2.first && second == other2.second; |
| } |
| else |
| { |
| return false; |
| } |
| } |
| |
| public override int GetHashCode() |
| { |
| return (int)((first ^ ((long)((ulong)first >> 32))) ^ (second ^ (second >> 32))); |
| } |
| } |
| |
| private static readonly long? NO_OUTPUT = new long?(0); |
| |
| private readonly bool doShare; |
| |
| private static readonly UpToTwoPositiveInt64Outputs singletonShare = new UpToTwoPositiveInt64Outputs(true); |
| private static readonly UpToTwoPositiveInt64Outputs singletonNoShare = new UpToTwoPositiveInt64Outputs(false); |
| |
| private UpToTwoPositiveInt64Outputs(bool doShare) |
| { |
| this.doShare = doShare; |
| } |
| |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| public static UpToTwoPositiveInt64Outputs GetSingleton(bool doShare) |
| { |
| return doShare ? singletonShare : singletonNoShare; |
| } |
| |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "This is a shipped public API")] |
| public long? Get(long v) |
| { |
| return v == 0 ? NO_OUTPUT : v; |
| } |
| |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "This is a shipped public API")] |
| public TwoInt64s Get(long first, long second) |
| { |
| return new TwoInt64s(first, second); |
| } |
| |
| public override object Common(object output1, object output2) |
| { |
| if (Debugging.AssertsEnabled) |
| { |
| Debugging.Assert(Valid(output1, false)); |
| Debugging.Assert(Valid(output2, false)); |
| } |
| long? output1_ = (long?)output1; |
| long? output2_ = (long?)output2; |
| if (output1_ == NO_OUTPUT || output2_ == NO_OUTPUT) |
| { |
| return NO_OUTPUT; |
| } |
| else if (doShare) |
| { |
| if (Debugging.AssertsEnabled) |
| { |
| Debugging.Assert(output1_ > 0); |
| Debugging.Assert(output2_ > 0); |
| } |
| return Math.Min(output1_.GetValueOrDefault(), output2_.GetValueOrDefault()); |
| } |
| else if (output1_.Equals(output2_)) |
| { |
| return output1_; |
| } |
| else |
| { |
| return NO_OUTPUT; |
| } |
| } |
| |
| public override object Subtract(object output, object inc) |
| { |
| if (Debugging.AssertsEnabled) |
| { |
| Debugging.Assert(Valid(output, false)); |
| Debugging.Assert(Valid(inc, false)); |
| } |
| long? output2 = (long?)output; |
| long? inc2 = (long?)inc; |
| if (Debugging.AssertsEnabled) Debugging.Assert(output2 >= inc2); |
| |
| if (inc2 == NO_OUTPUT) |
| { |
| return output2; |
| } |
| else if (output2.Equals(inc2)) |
| { |
| return NO_OUTPUT; |
| } |
| else |
| { |
| return output2 - inc2; |
| } |
| } |
| |
| public override object Add(object prefix, object output) |
| { |
| if (Debugging.AssertsEnabled) Debugging.Assert(Valid(prefix, false)); |
| if (Debugging.AssertsEnabled) Debugging.Assert(Valid(output, true)); |
| long? prefix2 = (long?)prefix; |
| if (output is long?) |
| { |
| long? output2 = (long?)output; |
| if (prefix2 == NO_OUTPUT) |
| { |
| return output2; |
| } |
| else if (output2 == NO_OUTPUT) |
| { |
| return prefix2; |
| } |
| else |
| { |
| return prefix2 + output2; |
| } |
| } |
| else |
| { |
| TwoInt64s output3 = (TwoInt64s)output; |
| long v = prefix2.Value; |
| return new TwoInt64s(output3.First + v, output3.Second + v); |
| } |
| } |
| |
| public override void Write(object output, DataOutput @out) |
| { |
| if (Debugging.AssertsEnabled) Debugging.Assert(Valid(output, true)); |
| if (output is long?) |
| { |
| long? output2 = (long?)output; |
| @out.WriteVInt64(output2.GetValueOrDefault() << 1); |
| } |
| else |
| { |
| TwoInt64s output3 = (TwoInt64s)output; |
| @out.WriteVInt64((output3.First << 1) | 1); |
| @out.WriteVInt64(output3.Second); |
| } |
| } |
| |
| public override object Read(DataInput @in) |
| { |
| long code = @in.ReadVInt64(); |
| if ((code & 1) == 0) |
| { |
| // single long |
| long v = (long)((ulong)code >> 1); |
| if (v == 0) |
| { |
| return NO_OUTPUT; |
| } |
| else |
| { |
| return v; |
| } |
| } |
| else |
| { |
| // two longs |
| long first = (long)((ulong)code >> 1); |
| long second = @in.ReadVInt64(); |
| return new TwoInt64s(first, second); |
| } |
| } |
| |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| private static bool Valid(long? o) // LUCENENET: CA1822: Mark members as static |
| { |
| Debugging.Assert(o != null); |
| Debugging.Assert(o is long?); |
| Debugging.Assert(o == NO_OUTPUT || o > 0); |
| return true; |
| } |
| |
| // Used only by assert |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| private static bool Valid(object o, bool allowDouble) // LUCENENET: CA1822: Mark members as static |
| { |
| if (!allowDouble) |
| { |
| Debugging.Assert(o is long?); |
| return Valid((long?)o); |
| } |
| else if (o is TwoInt64s) |
| { |
| return true; |
| } |
| else |
| { |
| return Valid((long?)o); |
| } |
| } |
| |
| public override object NoOutput => NO_OUTPUT; |
| |
| public override string OutputToString(object output) |
| { |
| return output.ToString(); |
| } |
| |
| [MethodImpl(MethodImplOptions.NoInlining)] |
| public override object Merge(object first, object second) |
| { |
| if (Debugging.AssertsEnabled) |
| { |
| Debugging.Assert(Valid(first, false)); |
| Debugging.Assert(Valid(second, false)); |
| } |
| return new TwoInt64s(((long?)first).GetValueOrDefault(), ((long?)second).GetValueOrDefault()); |
| } |
| } |
| } |