org.apfloat.internal.LongNTTConvolutionStepStrategy Maven / Gradle / Ivy
/*
* Apfloat arbitrary precision arithmetic library
* Copyright (C) 2002-2017 Mikko Tommila
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.apfloat.internal;
import org.apfloat.ApfloatRuntimeException;
import org.apfloat.spi.NTTConvolutionStepStrategy;
import org.apfloat.spi.DataStorage;
import static org.apfloat.internal.LongModConstants.*;
/**
* Steps of a three-NTT convolution for the long
type.
* This class implements the details of the element-by-element multiplication
* and element-by-element squaring of the transformed elements.
*
* The in-place multiplication and squaring of the data elements is done
* using a parallel algorithm, if the data fits in memory.
*
* All access to this class must be externally synchronized.
*
* @since 1.7.0
* @version 1.8.0
* @author Mikko Tommila
*/
public class LongNTTConvolutionStepStrategy
extends LongModMath
implements NTTConvolutionStepStrategy, Parallelizable
{
// Runnable for multiplying elements in place
private class MultiplyInPlaceRunnable
implements Runnable
{
public MultiplyInPlaceRunnable(DataStorage sourceAndDestination, DataStorage source, long offset, long length)
{
this.sourceAndDestination = sourceAndDestination;
this.source = source;
this.offset = offset;
this.length = length;
}
public void run()
{
DataStorage.Iterator dest = this.sourceAndDestination.iterator(DataStorage.READ_WRITE, this.offset, this.offset + this.length),
src = this.source.iterator(DataStorage.READ, this.offset, this.offset + this.length);
while (this.length > 0)
{
dest.setLong(modMultiply(dest.getLong(), src.getLong()));
dest.next();
src.next();
this.length--;
}
}
private DataStorage sourceAndDestination,
source;
private long offset,
length;
}
// Runnable for squaring elements in place
private class SquareInPlaceRunnable
implements Runnable
{
public SquareInPlaceRunnable(DataStorage sourceAndDestination, long offset, long length)
{
this.sourceAndDestination = sourceAndDestination;
this.offset = offset;
this.length = length;
}
public void run()
{
DataStorage.Iterator iterator = this.sourceAndDestination.iterator(DataStorage.READ_WRITE, this.offset, this.offset + this.length);
while (this.length > 0)
{
long value = iterator.getLong();
iterator.setLong(modMultiply(value, value));
iterator.next();
this.length--;
}
}
private DataStorage sourceAndDestination;
private long offset,
length;
}
/**
* Default constructor.
*/
public LongNTTConvolutionStepStrategy()
{
}
public void multiplyInPlace(DataStorage sourceAndDestination, DataStorage source, int modulus)
throws ApfloatRuntimeException
{
assert (sourceAndDestination != source);
long size = sourceAndDestination.getSize();
ParallelRunnable parallelRunnable = createMultiplyInPlaceParallelRunnable(sourceAndDestination, source, modulus);
if (size <= Integer.MAX_VALUE && // Only if the size fits in an integer, but with memory arrays it should
sourceAndDestination.isCached() && source.isCached()) // Only if the data storage supports efficient parallel random access
{
ParallelRunner.runParallel(parallelRunnable);
}
else
{
parallelRunnable.run(); // Just run in current thread without parallelization
}
}
public void squareInPlace(DataStorage sourceAndDestination, int modulus)
throws ApfloatRuntimeException
{
long size = sourceAndDestination.getSize();
ParallelRunnable parallelRunnable = createSquareInPlaceParallelRunnable(sourceAndDestination, modulus);
if (size <= Integer.MAX_VALUE && // Only if the size fits in an integer, but with memory arrays it should
sourceAndDestination.isCached()) // Only if the data storage supports efficient parallel random access
{
ParallelRunner.runParallel(parallelRunnable);
}
else
{
parallelRunnable.run(); // Just run in current thread without parallelization
}
}
/**
* Create a ParallelRunnable for multiplying the elements in-place.
*
* @param sourceAndDestination The first source data storage, which is also the destination.
* @param source The second source data storage.
* @param modulus Which modulus to use (0, 1, 2)
*
* @return An object suitable for multiplying the elements in parallel.
*/
protected ParallelRunnable createMultiplyInPlaceParallelRunnable(final DataStorage sourceAndDestination, final DataStorage source, int modulus)
{
final long size = sourceAndDestination.getSize();
setModulus(MODULUS[modulus]);
ParallelRunnable parallelRunnable = new ParallelRunnable(size)
{
@Override
public Runnable getRunnable(long offset, long length)
{
return new MultiplyInPlaceRunnable(sourceAndDestination, source, offset, length);
}
};
return parallelRunnable;
}
/**
* Create a ParallelRunnable for squaring the elements in-place.
*
* @param sourceAndDestination The source data storage, which is also the destination.
* @param modulus Which modulus to use (0, 1, 2)
*
* @return An object suitable for squaring the elements in parallel.
*/
protected ParallelRunnable createSquareInPlaceParallelRunnable(final DataStorage sourceAndDestination, int modulus)
{
final long size = sourceAndDestination.getSize();
setModulus(MODULUS[modulus]);
ParallelRunnable parallelRunnable = new ParallelRunnable(size)
{
@Override
public Runnable getRunnable(long offset, long length)
{
return new SquareInPlaceRunnable(sourceAndDestination, offset, length);
}
};
return parallelRunnable;
}
}