mirror of https://github.com/deavmi/niknaks
472 lines
9.2 KiB
D
472 lines
9.2 KiB
D
/**
|
|
* Arrays tooling
|
|
*
|
|
* Authors: Tristan Brice Velloza Kildaire (deavmi)
|
|
*/
|
|
module niknaks.arrays;
|
|
|
|
import niknaks.functional : Predicate;
|
|
|
|
/**
|
|
* Checks if the given value is present in
|
|
* the given array
|
|
*
|
|
* Params:
|
|
* array = the array to check against
|
|
* value = the value to check prescence
|
|
* for
|
|
* Returns: `true` if present, `false`
|
|
* otherwise
|
|
*/
|
|
public bool isPresent(T)(T[] array, T value)
|
|
{
|
|
if(array.length == 0)
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
foreach(T cur; array)
|
|
{
|
|
if(cur == value)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Tests the `isPresent!(T)(T[], T)` function
|
|
*
|
|
* Case: Non-empty array
|
|
*/
|
|
unittest
|
|
{
|
|
ubyte[] values = [1,2,3];
|
|
foreach(ubyte value; values)
|
|
{
|
|
assert(isPresent(values, value));
|
|
}
|
|
assert(isPresent(values, 0) == false);
|
|
assert(isPresent(values, 5) == false);
|
|
}
|
|
|
|
/**
|
|
* Tests the `isPresent!(T)(T[], T)` function
|
|
*
|
|
* Case: Empty array
|
|
*/
|
|
unittest
|
|
{
|
|
assert(isPresent([], 1) == false);
|
|
}
|
|
|
|
/**
|
|
* Given an array of values this tries to find
|
|
* the next free value of which is NOT present
|
|
* within the given array.
|
|
*
|
|
* If the array provided is emptied then a value
|
|
* will always be found and will be that of `T.init`.
|
|
*
|
|
* Params:
|
|
* used = the array of values
|
|
* found = the found free value
|
|
* Returns: `true` if a free value was found
|
|
* otherwise `false`
|
|
*/
|
|
@nogc
|
|
public bool findNextFree(T)(T[] used, ref T found) if(__traits(isIntegral, T))
|
|
{
|
|
// Temporary value used for searching
|
|
T tmp = T.init;
|
|
|
|
// If the array is empty then the first
|
|
// ... found value may as well be T.init
|
|
if(used.length == 0)
|
|
{
|
|
found = tmp;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// Is the starting value in use?
|
|
// If it is increment it such that
|
|
// ... looping infinite rule in the
|
|
// ... upcoming while loop works
|
|
// ... as expected
|
|
if(isPresent(used, tmp))
|
|
{
|
|
tmp++;
|
|
}
|
|
// If not, then we found it
|
|
else
|
|
{
|
|
found = tmp;
|
|
return true;
|
|
}
|
|
|
|
// Loop till we hit starting value
|
|
while(tmp != T.init)
|
|
{
|
|
if(isPresent(used, tmp))
|
|
{
|
|
tmp++;
|
|
}
|
|
else
|
|
{
|
|
found = tmp;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// We exited loop because we exhausted all possible values
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Tests the `findNextFree!(T)(T[], ref T)` function
|
|
*
|
|
* Case: First value is free + non-empty array
|
|
*/
|
|
unittest
|
|
{
|
|
ubyte[] values = [1,2,3];
|
|
|
|
ubyte free;
|
|
bool status = findNextFree(values, free);
|
|
assert(status == true);
|
|
assert(isPresent(values, free) == false);
|
|
}
|
|
|
|
/**
|
|
* Tests the `findNextFree!(T)(T[], ref T)` function
|
|
*
|
|
* Case: First value is unfree + non-empty array
|
|
*/
|
|
unittest
|
|
{
|
|
ubyte[] values = [0,2,3];
|
|
|
|
ubyte free;
|
|
bool status = findNextFree(values, free);
|
|
assert(status == true);
|
|
assert(isPresent(values, free) == false);
|
|
}
|
|
|
|
/**
|
|
* Tests the `findNextFree!(T)(T[], ref T)` function
|
|
*
|
|
* Case: Array is empty, first value should be T.init
|
|
*/
|
|
unittest
|
|
{
|
|
ubyte[] values = [];
|
|
|
|
ubyte free;
|
|
bool status = findNextFree(values, free);
|
|
assert(status == true);
|
|
assert(free == ubyte.init);
|
|
assert(isPresent(values, free) == false);
|
|
}
|
|
|
|
version(unittest)
|
|
{
|
|
import std.stdio : writeln;
|
|
}
|
|
|
|
/**
|
|
* Tests the `findNextFree!(T)(T[], ref T)` function
|
|
*
|
|
* Case: All values are unfree
|
|
*/
|
|
unittest
|
|
{
|
|
// Populate entire array with 0 through 255
|
|
ubyte[] values;
|
|
static foreach(ushort val; 0..256)
|
|
{
|
|
values~=val;
|
|
}
|
|
writeln(values);
|
|
|
|
ubyte free;
|
|
bool status = findNextFree(values, free);
|
|
assert(status == false);
|
|
|
|
// Ensure none of the values are present
|
|
foreach(ubyte i; values)
|
|
{
|
|
assert(isPresent(values, i) == true);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Filters items by the given predicate
|
|
*
|
|
* Params:
|
|
* filterIn = the array to filer
|
|
* predicate = the predicate to use
|
|
* filterOut = output array
|
|
*/
|
|
public void filter(T)(T[] filterIn, Predicate!(T) predicate, ref T[] filterOut)
|
|
{
|
|
foreach(T t; filterIn)
|
|
{
|
|
if(predicate(t))
|
|
{
|
|
filterOut ~= t;
|
|
}
|
|
}
|
|
}
|
|
|
|
version(unittest)
|
|
{
|
|
import niknaks.functional : predicateOf;
|
|
}
|
|
|
|
/**
|
|
* Tests the array filtering method
|
|
*/
|
|
unittest
|
|
{
|
|
bool onlyEven(int i)
|
|
{
|
|
return i % 2 == 0;
|
|
}
|
|
|
|
int[] vals = [0, 1, 2, 3];
|
|
|
|
int[] vals_expected = [0, 2];
|
|
int[] vals_got;
|
|
|
|
// TODO: See why not auto detecting the array type
|
|
filter!(int)(vals, predicateOf!(onlyEven), vals_got);
|
|
assert(vals_got == vals_expected);
|
|
}
|
|
|
|
/**
|
|
* Shifts a subset of the elements of
|
|
* the given array to a given position
|
|
* either from the left or right.
|
|
*
|
|
* Optionally allowing the shrinking
|
|
* of the array after the process,
|
|
* otherwise the last element shifted's
|
|
* previous value will be set to the
|
|
* value specified.
|
|
*
|
|
* Params:
|
|
* array = the input array
|
|
* position = the position to shift
|
|
* onto
|
|
* rightwards = if `true` then shift
|
|
* elements into the position rightwards,
|
|
* else leftwards (which is also the default)
|
|
* shrink = if set to `true` then
|
|
* the array will be resized to exclude
|
|
* the now "empty" element
|
|
* filler = the value to place in
|
|
* the space where the last element
|
|
* shifted no longer occupies, by default
|
|
* this is `T.init`
|
|
* Returns: the shifted array
|
|
*/
|
|
public T[] shiftInto(T)(T[] array, size_t position, bool rightwards = false, bool shrink = false, T filler = T.init)
|
|
{
|
|
// Out of range
|
|
if(position >= array.length)
|
|
{
|
|
return array;
|
|
}
|
|
|
|
// if rightwards
|
|
if(rightwards)
|
|
{
|
|
// nothing further left than index 0
|
|
if(!position)
|
|
{
|
|
return array;
|
|
}
|
|
|
|
for(size_t i = position; i > 0; i--)
|
|
{
|
|
array[i] = array[i-1];
|
|
}
|
|
|
|
// no shrink, then fill with filler
|
|
if(!shrink)
|
|
{
|
|
array[0] = filler;
|
|
}
|
|
// chomp left-hand side
|
|
else
|
|
{
|
|
array = array[1..$];
|
|
}
|
|
}
|
|
// if leftwards
|
|
else
|
|
{
|
|
// nothing furtherright
|
|
if(position == array.length-1)
|
|
{
|
|
return array;
|
|
}
|
|
|
|
for(size_t i = position; i < array.length-1; i++)
|
|
{
|
|
array[i] = array[i+1];
|
|
}
|
|
|
|
// no shrink, then fill with filler
|
|
if(!shrink)
|
|
{
|
|
array[$-1] = filler;
|
|
}
|
|
// chomp right-hand side
|
|
else
|
|
{
|
|
array = array[0..$-1];
|
|
}
|
|
}
|
|
|
|
|
|
return array;
|
|
}
|
|
|
|
/**
|
|
* Rightwards shifting into
|
|
*
|
|
* See_Also: `shiftInto`
|
|
*/
|
|
public T[] shiftIntoRightwards(T)(T[] array, size_t position, bool shrink = false)
|
|
{
|
|
return shiftInto(array, position, true, shrink);
|
|
}
|
|
|
|
/**
|
|
* Tests the rightwards shifting
|
|
*/
|
|
unittest
|
|
{
|
|
int[] numbas = [1, 5, 2];
|
|
numbas = numbas.shiftIntoRightwards(1);
|
|
|
|
// should now be [0, 1, 2]
|
|
writeln(numbas);
|
|
assert(numbas == [0, 1, 2]);
|
|
|
|
numbas = [1, 5, 2];
|
|
numbas = numbas.shiftIntoRightwards(0);
|
|
|
|
// should now be [1, 5, 2]
|
|
writeln(numbas);
|
|
assert(numbas == [1, 5, 2]);
|
|
|
|
numbas = [1, 5, 2];
|
|
numbas = numbas.shiftIntoRightwards(2);
|
|
|
|
// should now be [0, 1, 5]
|
|
writeln(numbas);
|
|
assert(numbas == [0, 1, 5]);
|
|
|
|
numbas = [1, 2];
|
|
numbas = numbas.shiftIntoRightwards(1);
|
|
|
|
// should now be [0, 1]
|
|
writeln(numbas);
|
|
assert(numbas == [0, 1]);
|
|
|
|
numbas = [1, 2];
|
|
numbas = numbas.shiftIntoRightwards(0);
|
|
|
|
// should now be [1, 2]
|
|
writeln(numbas);
|
|
assert(numbas == [1, 2]);
|
|
|
|
numbas = [];
|
|
numbas = numbas.shiftIntoRightwards(0);
|
|
|
|
// should now be []
|
|
writeln(numbas);
|
|
assert(numbas == []);
|
|
|
|
numbas = [1, 5, 2];
|
|
numbas = numbas.shiftIntoRightwards(1, true);
|
|
|
|
// should now be [1, 2]
|
|
writeln(numbas);
|
|
assert(numbas == [1, 2]);
|
|
}
|
|
|
|
/**
|
|
* Leftwards shifting into
|
|
*
|
|
* See_Also: `shiftInto`
|
|
*/
|
|
public T[] shiftIntoLeftwards(T)(T[] array, size_t position, bool shrink = false)
|
|
{
|
|
return shiftInto(array, position, false, shrink);
|
|
}
|
|
|
|
/**
|
|
* Tests the leftwards shifting
|
|
*/
|
|
unittest
|
|
{
|
|
int[] numbas = [1, 5, 2];
|
|
numbas = numbas.shiftIntoLeftwards(1);
|
|
|
|
// should now be [1, 2, 0]
|
|
writeln(numbas);
|
|
assert(numbas == [1, 2, 0]);
|
|
|
|
numbas = [1, 5, 2];
|
|
numbas = numbas.shiftIntoLeftwards(0);
|
|
|
|
// should now be [5, 2, 0]
|
|
writeln(numbas);
|
|
assert(numbas == [5, 2, 0]);
|
|
|
|
numbas = [1, 5, 2];
|
|
numbas = numbas.shiftIntoLeftwards(2);
|
|
|
|
// should now be [1, 5, 2]
|
|
writeln(numbas);
|
|
assert(numbas == [1, 5, 2]);
|
|
|
|
numbas = [];
|
|
numbas = numbas.shiftIntoLeftwards(0);
|
|
|
|
// should now be []
|
|
writeln(numbas);
|
|
assert(numbas == []);
|
|
|
|
numbas = [1, 5, 2];
|
|
numbas = numbas.shiftIntoLeftwards(1, true);
|
|
|
|
// should now be [1, 2]
|
|
writeln(numbas);
|
|
assert(numbas == [1, 2]);
|
|
}
|
|
|
|
/**
|
|
* Removes the element at the
|
|
* provided position in the
|
|
* given array
|
|
*
|
|
* Params:
|
|
* array = the array
|
|
* position = position of
|
|
* element to remove
|
|
* Returns: the array
|
|
*/
|
|
public T[] removeResize(T)(T[] array, size_t position)
|
|
{
|
|
return array.shiftInto(position, false, true);
|
|
} |