blob: 75f9cd60cf59ecf50d9f192284712c3d4e8d6335 [file] [log] [blame]
using J2N.Numerics;
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 ^ (first.TripleShift(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 = code.TripleShift(1);
if (v == 0)
{
return NO_OUTPUT;
}
else
{
return v;
}
}
else
{
// two longs
long first = code.TripleShift(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());
}
}
}