New Tapi package dealing with frequency BitSets 37/112437/22
authorJoakim Törnqvist <joakim.tornqvist@smartoptics.com>
Wed, 3 Jul 2024 10:25:45 +0000 (12:25 +0200)
committerGilles Thouenon <gilles.thouenon@orange.com>
Fri, 6 Sep 2024 20:27:20 +0000 (22:27 +0200)
Refactored code creating classes capable of converting
byte to BitSet to numeric frequency ranges.

e.g. byte[] = {-1} -> 191.325:191.375 THz

Each byte in an array is treated as 8 bits. Meaning an array
of 96 bytes is treated as a BitSet of 768 bits. The bits may
subsequently be converted to a map where the key is the
lower frequency and the value is the upper frequency in a range.

The package treats each signed byte at "face value". FlexGrid
or FixGrid data are treated equally.

Examples
byte[] = {1}       -> BitSet: {0}               -> 191.325:191.33125
byte[] = {15}      -> Bitset: {0,1,2,3}         -> 191.325:191.35
byte[] = {-16}     -> BitSet: {4,5,6,7}         -> 191.35:191.375
byte[] = {-1}      -> BitSet: {0,1,2,3,4,5,6,7} -> 191.325:191.375
byte[] = {-128}    -> BitSet: {7}               -> 191.36875:191.375
byte[] = {-128, 1} -> BitSet: {7,8}             -> 191.36875:191.38125

Package Overview

The main parts of this package are the classes AvailableGrid and
NumericFrequency. AvailableGrid is used to ease converting
a byte array to either assigned (used) or available frequencies
represented by a BitSet.

AvailableGridFactory provides a way to instantiate an AvailableGrid
object using either available or used frequency grid byte data.

The method getFreqMapFromBitSet in ConvertORToTapiTopology has
been modified and copied to NumericFrequency and is used to
convert a bitset to a Map<Double, Double> (numeric frequency range).

The interface Math is intended to make it easier to unit test the code.
The implementation of Math (i.e. FrequencyMath) simply wraps calls to
GridUtils.getStartFrequencyFromIndex(...) and
GridUtils.getStopFrequencyFromIndex(...).
Since GridUtils depends on global constants, any changes to those
constants may result in unit tests failing. The interface 'Math'
provides a way to test the code using predictable data.

Example usage:

/**
 * This method converts a byte array (e.g. 96 elements) representing
 * frequency ranges into a BitSet (i.e. 768 bits), and finally into
 * a Map<Double, Double> where the key represents the start frequency
 * and the value the end frequency in a range.
 *
 * In short, this example will convert...
 *     byte[] = {-1}
 * ...into...
 *      Map<Double, Double> = 191.325:191.375
 */
public Map<Double, Double> availableMap(byte[] availableByteMap) {

    Available available = new AvailableGrid(availableByteMap);

    // This object will help us convert an instance of 'Available'
    // into a numeric frequency range.
    Numeric numericFrequency = new NumericFrequency(
            GridConstant.START_EDGE_FREQUENCY,
            GridConstant.EFFECTIVE_BITS,
            new FrequencyMath()
    );

    // Convert BitSet data contained in the bitMap object
    // into a frequency range map.
    // Note: If instead we want the assigned frequencies
    //       we could execute:
    //       numericFrequency.assignedFrequency(available):
    return numericFrequency.availableFrequency(available);
}

JIRA: TRNSPRTPCE-802
Change-Id: I578abad37351eb4ead5e8907207f1384df27f09a
Signed-off-by: Joakim Törnqvist <joakim.tornqvist@smartoptics.com>
tapi/src/main/java/org/opendaylight/transportpce/tapi/frequency/Available.java [new file with mode: 0644]
tapi/src/main/java/org/opendaylight/transportpce/tapi/frequency/AvailableGrid.java [new file with mode: 0644]
tapi/src/main/java/org/opendaylight/transportpce/tapi/frequency/AvailableGridFactory.java [new file with mode: 0644]
tapi/src/main/java/org/opendaylight/transportpce/tapi/frequency/Factory.java [new file with mode: 0644]
tapi/src/main/java/org/opendaylight/transportpce/tapi/frequency/FrequencyMath.java [new file with mode: 0644]
tapi/src/main/java/org/opendaylight/transportpce/tapi/frequency/Math.java [new file with mode: 0644]
tapi/src/main/java/org/opendaylight/transportpce/tapi/frequency/Numeric.java [new file with mode: 0644]
tapi/src/main/java/org/opendaylight/transportpce/tapi/frequency/NumericFrequency.java [new file with mode: 0644]
tapi/src/test/java/org/opendaylight/transportpce/tapi/frequency/AvailableGridFactoryTest.java [new file with mode: 0644]
tapi/src/test/java/org/opendaylight/transportpce/tapi/frequency/AvailableGridTest.java [new file with mode: 0644]
tapi/src/test/java/org/opendaylight/transportpce/tapi/frequency/NumericFrequencyTest.java [new file with mode: 0644]

diff --git a/tapi/src/main/java/org/opendaylight/transportpce/tapi/frequency/Available.java b/tapi/src/main/java/org/opendaylight/transportpce/tapi/frequency/Available.java
new file mode 100644 (file)
index 0000000..18cda27
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Copyright © 2024 Smartoptics and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.transportpce.tapi.frequency;
+
+import java.util.BitSet;
+import java.util.Map;
+
+/**
+ * A class capable of representing available frequency slots as a bit map may implement
+ * this interface.
+ *
+ * <p>
+ * Available frequency ranges are represented by a (signed) byte array.
+ * The size of the byte array multiplied by byte size is equal to the
+ * total number of frequency "slots".
+ * e.g. 96 bytes = 768 bits = 768 frequency slots.
+ *
+ * <p>
+ * Each frequency range is assumed to be 1 signed byte, i.e. 8 bits where
+ * each bit represents a frequency range with a width of e.g. 6.25 GHz.
+ *
+ * <p>
+ * Examples:
+ * byte[] frequencies = {0}       => {0b00000000} (no frequency slots available)
+ * byte[] frequencies = {1}       => {0b00000001}
+ * byte[] frequencies = {56}      => {0b00111000}
+ * byte[] frequencies = {127}     => {0b01111111}
+ * byte[] frequencies = {-128}    => {0b10000000}
+ * byte[] frequencies = {-1}      => {0b11111111} (all frequency slots available)
+ * byte[] frequencies = {-58}     => {0b11000110}
+ * byte[] frequencies = {-58, -1} => {0b11000110, 0b11111111}
+ *
+ * <p>
+ * The available frequency slots...
+ *     byte[] frequencies = {-58, -1} => {0b11000110, 0b11111111}
+ * ...can be reversed using the method assignedFrequencies():
+ *     availableFrequencies() => {0b00111001, 0b00000000}
+ */
+public interface Available {
+
+    /**
+     * Available frequency ranges.
+     *
+     * <p>
+     * This method returns a byte array representing available frequency ranges.
+     *
+     * @return A byte array representing available frequency ranges.
+     */
+    byte[] availableFrequencyRanges();
+
+    /**
+     * Assigned frequencies.
+     *
+     * <p>
+     * This method returns a BitSet representing used frequencies.
+     *
+     * <p>
+     * Each bit in the set represents a frequency range.
+     *  - 1 (true) indicates that the frequency range is assigned (used).
+     *  - 0 (false) indicates that the frequency range is available.
+     *
+     * @return A BitSet representing used frequency ranges.
+     */
+    BitSet assignedFrequencies();
+
+    /**
+     * Available frequencies.
+     *
+     * <p>
+     * This method returns a BitSet representing available frequencies.
+     * This is the equivalent of inverting a bitset of used frequencies,
+     * i.e. inverting the result of the method assignedFrequencies().
+     *
+     * <p>
+     * Each bit in the set represents a frequency.
+     *  - 1 (true) indicates that the frequency range is available.
+     *  - 0 (false) indicates that the frequency range is unavailable (used).
+     *
+     * @return A BitSet representing available frequency ranges.
+     */
+    BitSet availableFrequencies();
+
+    /**
+     * Return a map of ASSIGNED (i.e. used) frequency ranges.
+     *
+     * <p>
+     * The key is the lower frequency bound and the value is the upper frequency bound.
+     */
+    Map<Double, Double> assignedFrequency(Numeric numeric);
+
+    /**
+     * Return a map of AVAILABLE frequency ranges.
+     *
+     * <p>
+     * The key is the lower frequency bound and the value is the upper frequency bound.
+     */
+    Map<Double, Double> availableFrequency(Numeric numeric);
+
+}
diff --git a/tapi/src/main/java/org/opendaylight/transportpce/tapi/frequency/AvailableGrid.java b/tapi/src/main/java/org/opendaylight/transportpce/tapi/frequency/AvailableGrid.java
new file mode 100644 (file)
index 0000000..0f1d666
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright © 2024 Smartoptics and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.transportpce.tapi.frequency;
+
+import java.util.BitSet;
+import java.util.Map;
+
+public class AvailableGrid implements Available {
+
+    private final byte[] availableFrequencyGrid;
+
+    /**
+     * The available frequency slots are represented by a (signed) byte array.
+     *
+     * <p>
+     * The size of the byte array multiplied by byte size is equal to the
+     * total number of frequency slots.
+     * e.g. 96 bytes = 768 bits = 768 frequency slots.
+     *
+     * <p>
+     * The available frequency slots...
+     *     byte[] frequencies = {-58, -1} => {0b11000110, 0b11111111}
+     * ...can be reversed using the method assignedFrequencies():
+     *     availableFrequencies() => {0b00111001, 0b00000000}
+     */
+    public AvailableGrid(byte[] availableFrequencyGrid) {
+        this.availableFrequencyGrid = new byte[availableFrequencyGrid.length];
+        System.arraycopy(availableFrequencyGrid, 0, this.availableFrequencyGrid, 0, availableFrequencyGrid.length);
+    }
+
+    @Override
+    public byte[] availableFrequencyRanges() {
+
+        byte[] freqBitSetCopy = new byte[availableFrequencyGrid.length];
+
+        System.arraycopy(availableFrequencyGrid, 0, freqBitSetCopy, 0, availableFrequencyGrid.length);
+
+        return freqBitSetCopy;
+    }
+
+    @Override
+    public BitSet assignedFrequencies() {
+
+        BitSet bitSet = new BitSet(availableFrequencyGrid.length * Byte.SIZE);
+
+        bitSet.or(BitSet.valueOf(availableFrequencyGrid));
+
+        bitSet.flip(0, availableFrequencyGrid.length * Byte.SIZE);
+
+        return bitSet;
+    }
+
+    @Override
+    public BitSet availableFrequencies() {
+
+        BitSet bitSet = new BitSet(availableFrequencyGrid.length * Byte.SIZE);
+
+        bitSet.or(BitSet.valueOf(availableFrequencyGrid));
+
+        return bitSet;
+
+    }
+
+    @Override
+    public Map<Double, Double> assignedFrequency(Numeric numeric) {
+        return numeric.assignedFrequency(this);
+    }
+
+    @Override
+    public Map<Double, Double> availableFrequency(Numeric numeric) {
+        return numeric.availableFrequency(this);
+    }
+}
diff --git a/tapi/src/main/java/org/opendaylight/transportpce/tapi/frequency/AvailableGridFactory.java b/tapi/src/main/java/org/opendaylight/transportpce/tapi/frequency/AvailableGridFactory.java
new file mode 100644 (file)
index 0000000..6dbe6bf
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright © 2024 Smartoptics and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.transportpce.tapi.frequency;
+
+
+public class AvailableGridFactory implements Factory {
+
+    @Override
+    public Available fromAvailable(byte[] frequencyRange) {
+        return new AvailableGrid(frequencyRange);
+    }
+
+    @Override
+    public Available fromAssigned(byte[] frequencyRange) {
+        byte[] frequencyRangeCopy = new byte[frequencyRange.length];
+
+        for (int i = 0; i < frequencyRange.length; i++) {
+            frequencyRangeCopy[i] = (byte) ~frequencyRange[i];
+        }
+
+        return new AvailableGrid(frequencyRangeCopy);
+    }
+
+}
diff --git a/tapi/src/main/java/org/opendaylight/transportpce/tapi/frequency/Factory.java b/tapi/src/main/java/org/opendaylight/transportpce/tapi/frequency/Factory.java
new file mode 100644 (file)
index 0000000..c178a95
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright © 2024 Smartoptics and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.transportpce.tapi.frequency;
+
+
+public interface Factory {
+
+    /**
+     * Creates an instance of Available, treating
+     * the byte array as an available frequency range.
+     */
+    Available fromAvailable(byte[] frequencyRange);
+
+    /**
+     * Creates an instance of Available, treating
+     * the byte array as an assigned (used) frequency range.
+     */
+    Available fromAssigned(byte[] frequencyRange);
+
+}
diff --git a/tapi/src/main/java/org/opendaylight/transportpce/tapi/frequency/FrequencyMath.java b/tapi/src/main/java/org/opendaylight/transportpce/tapi/frequency/FrequencyMath.java
new file mode 100644 (file)
index 0000000..b4bf0ef
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright © 2024 Smartoptics and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.transportpce.tapi.frequency;
+
+import org.opendaylight.transportpce.common.fixedflex.GridUtils;
+
+public class FrequencyMath implements Math {
+    @Override
+    public Double getStartFrequencyFromIndex(int index) {
+        return GridUtils.getStartFrequencyFromIndex(index).doubleValue();
+    }
+
+    @Override
+    public Double getStopFrequencyFromIndex(int index) {
+        return GridUtils.getStopFrequencyFromIndex(index).doubleValue();
+    }
+}
diff --git a/tapi/src/main/java/org/opendaylight/transportpce/tapi/frequency/Math.java b/tapi/src/main/java/org/opendaylight/transportpce/tapi/frequency/Math.java
new file mode 100644 (file)
index 0000000..b4e8632
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright © 2024 Smartoptics and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.transportpce.tapi.frequency;
+
+public interface Math {
+
+    /**
+     * Compute the start frequency in THz for the given index.
+     *
+     * @param index int
+     * @return the start frequency in THz for the provided index.
+     */
+    Double getStartFrequencyFromIndex(int index);
+
+    /**
+     * Compute the stop frequency in THz for the given index.
+     *
+     * @param index int
+     * @return the stop frequency in THz for the provided index.
+     */
+    Double getStopFrequencyFromIndex(int index);
+
+}
diff --git a/tapi/src/main/java/org/opendaylight/transportpce/tapi/frequency/Numeric.java b/tapi/src/main/java/org/opendaylight/transportpce/tapi/frequency/Numeric.java
new file mode 100644 (file)
index 0000000..1d3ee4b
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright © 2024 Smartoptics and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.transportpce.tapi.frequency;
+
+import java.util.Map;
+
+public interface Numeric {
+
+    /**
+     * Return a map of ASSIGNED (i.e. used) frequency ranges.
+     *
+     * <p>
+     * The key is the lower frequency bound and the value is the upper frequency bound.
+     */
+    Map<Double, Double> assignedFrequency(Available frequency);
+
+    /**
+     * Return a map of AVAILABLE frequency ranges.
+     *
+     * <p>
+     * The key is the lower frequency bound and the value is the upper frequency bound.
+     */
+    Map<Double, Double> availableFrequency(Available frequency);
+
+}
diff --git a/tapi/src/main/java/org/opendaylight/transportpce/tapi/frequency/NumericFrequency.java b/tapi/src/main/java/org/opendaylight/transportpce/tapi/frequency/NumericFrequency.java
new file mode 100644 (file)
index 0000000..9144682
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright © 2024 Smartoptics, Orange and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.transportpce.tapi.frequency;
+
+import java.util.BitSet;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class NumericFrequency implements Numeric {
+
+    private final Double startFrequency;
+
+    private final int nrOfBits;
+
+    private final Math math;
+
+    public NumericFrequency(Double startFrequency, int nrOfBits) {
+        this(startFrequency, nrOfBits, new FrequencyMath());
+    }
+
+    public NumericFrequency(Double startFrequency, int nrOfBits, Math math) {
+        this.startFrequency = startFrequency;
+        this.nrOfBits = nrOfBits;
+        this.math = math;
+    }
+
+    @Override
+    public Map<Double, Double> assignedFrequency(Available frequency) {
+        return this.range(frequency.assignedFrequencies());
+    }
+
+    @Override
+    public Map<Double, Double> availableFrequency(Available frequency) {
+        return this.range(frequency.availableFrequencies());
+    }
+
+    /**
+     * Note: This code is mostly copied as is from the original
+     * class ConvertORToTapiTopology.java as part of refactoring.
+     */
+    private Map<Double, Double> range(BitSet bitSet) {
+
+        Map<Double,Double> freqMap = new LinkedHashMap<>();
+
+        Double startFreq = startFrequency;
+        Double stopFreq = 0.0;
+        boolean occupied = !bitSet.get(0);
+
+        for (int index = 0 ; index < nrOfBits ; index++) {
+            if (occupied) {
+                if (bitSet.get(index)) {
+                    startFreq = math.getStartFrequencyFromIndex(index);
+                    stopFreq = math.getStartFrequencyFromIndex(index);
+                    occupied = false;
+                }
+            } else {
+                if (!bitSet.get(index)) {
+                    stopFreq = math.getStartFrequencyFromIndex(index);
+                    occupied = true;
+                }
+            }
+            if (occupied) {
+                if (stopFreq > startFreq) {
+                    freqMap.put(startFreq, stopFreq);
+                    startFreq = stopFreq;
+                }
+            } else {
+                if (index == nrOfBits - 1
+                        && Double.compare(startFreq, stopFreq) == 0) {
+                    stopFreq = math.getStopFrequencyFromIndex(index);
+                    freqMap.put(startFreq, stopFreq);
+                }
+            }
+        }
+        return freqMap;
+    }
+}
diff --git a/tapi/src/test/java/org/opendaylight/transportpce/tapi/frequency/AvailableGridFactoryTest.java b/tapi/src/test/java/org/opendaylight/transportpce/tapi/frequency/AvailableGridFactoryTest.java
new file mode 100644 (file)
index 0000000..b18f178
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright © 2024 Smartoptics and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.transportpce.tapi.frequency;
+
+
+import java.util.BitSet;
+import org.junit.Assert;
+import org.junit.jupiter.api.Test;
+
+class AvailableGridFactoryTest {
+
+    @Test
+    void assignedFrequency() {
+        AvailableGridFactory availableGridFactory = new AvailableGridFactory();
+        byte[] frequency = {0, -1, -1, -1, -1, -1, -1, -1};
+        byte[] expected =  {0, -1, -1, -1, -1, -1, -1, -1};
+
+        Assert.assertArrayEquals(
+                expected,
+                availableGridFactory.fromAvailable(frequency).availableFrequencyRanges()
+        );
+    }
+
+    @Test
+    void availableFrequency() {
+        AvailableGridFactory availableGridFactory = new AvailableGridFactory();
+
+        byte[] assignedFrequencies          =  {-1,  0,  0,  0,  0,  0,  0,  0};
+        byte[] expectedAvailableFrequencies =  { 0, -1, -1, -1, -1, -1, -1, -1};
+
+        Available bitMap = availableGridFactory.fromAssigned(assignedFrequencies);
+
+        byte[] availableFrequencies = bitMap.availableFrequencyRanges();
+
+        Assert.assertArrayEquals(
+                expectedAvailableFrequencies,
+                availableFrequencies
+        );
+
+        Assert.assertArrayEquals(
+                availableFrequencies,
+                bitMap.availableFrequencyRanges()
+        );
+
+        Assert.assertEquals(
+                BitSet.valueOf(assignedFrequencies),
+                bitMap.assignedFrequencies()
+        );
+
+    }
+}
\ No newline at end of file
diff --git a/tapi/src/test/java/org/opendaylight/transportpce/tapi/frequency/AvailableGridTest.java b/tapi/src/test/java/org/opendaylight/transportpce/tapi/frequency/AvailableGridTest.java
new file mode 100644 (file)
index 0000000..dfc7fab
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Copyright © 2024 Smartoptics and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.transportpce.tapi.frequency;
+
+import java.util.Arrays;
+import java.util.BitSet;
+import org.junit.Assert;
+import org.junit.jupiter.api.Test;
+
+class AvailableGridTest {
+
+    @Test
+    void testSize() {
+        byte[] frequencies = {0, -1, -1, 0, 0, 0, 0, -1};
+
+        AvailableGrid availableGrid = new AvailableGrid(frequencies);
+
+        Assert.assertEquals(64, availableGrid.assignedFrequencies().size());
+        Assert.assertEquals(64, availableGrid.availableFrequencies().size());
+    }
+
+    @Test
+    void availableFrequencyRanges() {
+        byte[] frequencies = {0, -1, -1, 0, 0, 0, 0, -1};
+        byte[] expected = {0, -1, -1, 0, 0, 0, 0, -1};
+
+        AvailableGrid availableGrid = new AvailableGrid(frequencies);
+
+        Assert.assertArrayEquals(expected, availableGrid.availableFrequencyRanges());
+    }
+
+    @Test
+    void availableFrequencyRangesAreInMutable() {
+        byte[] frequencies = {0, -1, -1, 0, 0, 0, 0, -1};
+        byte[] expected = {0, -1, -1, 0, 0, 0, 0, -1};
+
+        AvailableGrid availableGrid = new AvailableGrid(frequencies);
+
+        //This change shouldn't affect the contents of the assignedFrequencies object
+        frequencies[0] = -1;
+
+        //The arrays are different
+        Assert.assertFalse(Arrays.equals(expected, frequencies));
+
+        //The contents of the assignedFrequencies object are still the same
+        Assert.assertArrayEquals(expected, availableGrid.availableFrequencyRanges());
+    }
+
+
+    @Test
+    void assignedFrequenciesStart() {
+        byte[] frequencies = {-1, 0, 0, 0, 0, 0, 0, 0};
+        BitSet expected = new BitSet();
+        expected.set(0, 8);
+
+        AvailableGrid availableGrid = new AvailableGrid(frequencies);
+
+        Assert.assertEquals(expected, availableGrid.availableFrequencies());
+    }
+
+    @Test
+    void assignedFrequenciesMiddle() {
+        byte[] frequencies = {0, -1, 0, 0, 0, 0, 0, 0};
+        BitSet expected = new BitSet();
+        expected.set(8, 16);
+
+        AvailableGrid availableGrid = new AvailableGrid(frequencies);
+
+        Assert.assertEquals(expected, availableGrid.availableFrequencies());
+    }
+
+    @Test
+    void assignedFrequenciesConsecutive() {
+        byte[] frequencies = {0, -1, -1, 0, 0, 0, 0, 0};
+        BitSet expected = new BitSet();
+        expected.set(8, 24);
+
+        AvailableGrid availableGrid = new AvailableGrid(frequencies);
+
+        Assert.assertEquals(expected, availableGrid.availableFrequencies());
+    }
+
+    @Test
+    void assignedFrequenciesEnd() {
+        byte[] frequencies = {0, 0, 0, 0, 0, 0, 0, -1};
+        BitSet expected = new BitSet();
+        expected.set(56, 64);
+
+        AvailableGrid availableGrid = new AvailableGrid(frequencies);
+
+        Assert.assertEquals(expected, availableGrid.availableFrequencies());
+    }
+
+    @Test
+    void assignedFrequenciesPartial() {
+        byte[] frequencies = {0, 0, 0, 0, 0, 0, 0, 56};
+        BitSet expected = new BitSet();
+        expected.set(59, 62);
+
+        AvailableGrid availableGrid = new AvailableGrid(frequencies);
+
+        Assert.assertEquals(expected, availableGrid.availableFrequencies());
+    }
+
+    @Test
+    void assignedFrequenciesJoined() {
+        byte[] frequencies = {0, -128, 7, 0, 0, 0, 0, 0};
+        BitSet expected = new BitSet();
+        expected.set(15, 19);
+
+        AvailableGrid availableGrid = new AvailableGrid(frequencies);
+
+        Assert.assertEquals(expected, availableGrid.availableFrequencies());
+    }
+
+    @Test
+    void assignedFrequenciesAll() {
+        byte[] frequencies = {-1, -1, -1, -1, -1, -1, -1, -1};
+        BitSet expected = new BitSet();
+        expected.set(0, 64);
+
+        AvailableGrid availableGrid = new AvailableGrid(frequencies);
+
+        Assert.assertEquals(expected, availableGrid.availableFrequencies());
+    }
+
+    @Test
+    void assignedFrequenciesNone() {
+        byte[] frequencies = {0, 0, 0, 0, 0, 0, 0, 0};
+        BitSet expected = new BitSet();
+
+        AvailableGrid availableGrid = new AvailableGrid(frequencies);
+
+        Assert.assertEquals(expected, availableGrid.availableFrequencies());
+    }
+
+    @Test
+    void availableFrequencies() {
+        byte[] frequencies = {0, -1, 0, 0, 0, 0, 0, 0};
+
+        BitSet expected = new BitSet();
+        expected.set(0, 8);
+        expected.set(16, 64);
+
+        AvailableGrid availableGrid = new AvailableGrid(frequencies);
+
+        BitSet assigned = availableGrid.assignedFrequencies();
+        Assert.assertEquals(expected, assigned);
+
+        BitSet available = availableGrid.availableFrequencies();
+        Assert.assertEquals(BitSet.valueOf(frequencies), available);
+
+        Assert.assertNotEquals(available, assigned);
+    }
+
+}
\ No newline at end of file
diff --git a/tapi/src/test/java/org/opendaylight/transportpce/tapi/frequency/NumericFrequencyTest.java b/tapi/src/test/java/org/opendaylight/transportpce/tapi/frequency/NumericFrequencyTest.java
new file mode 100644 (file)
index 0000000..464cb00
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Copyright © 2024 Smartoptics and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.transportpce.tapi.frequency;
+
+import java.util.BitSet;
+import java.util.Map;
+import org.junit.Assert;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+class NumericFrequencyTest {
+
+    @Test
+    void assignedFrequency() {
+        BitSet assigned = new BitSet(768);
+        assigned.set(760, 768);
+
+        Available used = Mockito.mock(Available.class);
+        Mockito.when(used.assignedFrequencies()).thenReturn(assigned);
+
+        Math math = Mockito.mock(Math.class);
+        Mockito.when(math.getStartFrequencyFromIndex(760)).thenReturn(196.075);
+        Mockito.when(math.getStopFrequencyFromIndex(767)).thenReturn(196.125);
+
+        Map<Double, Double> expected = Map.of(196.075, 196.125);
+
+        Numeric frequencyService = new NumericFrequency(191.325, 768, math);
+        Assert.assertEquals(expected, frequencyService.assignedFrequency(used));
+
+    }
+
+    @Test
+    void assignedStartAndEndFrequencyRanges() {
+        Math math = Mockito.mock(Math.class);
+        Mockito.when(math.getStartFrequencyFromIndex(8)).thenReturn(191.375);
+        Mockito.when(math.getStartFrequencyFromIndex(760)).thenReturn(196.075);
+        Mockito.when(math.getStopFrequencyFromIndex(767)).thenReturn(196.125);
+
+        BitSet assigned = new BitSet(768);
+        assigned.set(0, 8);
+        assigned.set(760, 768);
+
+        Available used = Mockito.mock(Available.class);
+        Mockito.when(used.assignedFrequencies()).thenReturn(assigned);
+
+        Map<Double, Double> expected = Map.of(191.325, 191.375, 196.075, 196.125);
+
+        Numeric frequencyService = new NumericFrequency(191.325, 768, math);
+        Assert.assertEquals(expected, frequencyService.assignedFrequency(used));
+
+    }
+
+    @Test
+    void assignedMiddleFrequencyRanges() {
+        Math math = Mockito.mock(Math.class);
+        Mockito.when(math.getStartFrequencyFromIndex(32)).thenReturn(191.525);
+        Mockito.when(math.getStartFrequencyFromIndex(40)).thenReturn(191.575);
+
+        BitSet assigned = new BitSet(768);
+        assigned.set(32, 40);
+
+        Available used = Mockito.mock(Available.class);
+        Mockito.when(used.assignedFrequencies()).thenReturn(assigned);
+
+        Map<Double, Double> expected = Map.of(191.525, 191.575);
+
+        Numeric frequencyService = new NumericFrequency(191.325, 768, math);
+        Assert.assertEquals(expected, frequencyService.assignedFrequency(used));
+
+    }
+
+    @Test
+    void assignedStartMiddleAndEndFrequencyRanges() {
+        Math math = Mockito.mock(Math.class);
+        Mockito.when(math.getStartFrequencyFromIndex(8)).thenReturn(191.375);
+        Mockito.when(math.getStartFrequencyFromIndex(32)).thenReturn(191.525);
+        Mockito.when(math.getStartFrequencyFromIndex(40)).thenReturn(191.575);
+        Mockito.when(math.getStartFrequencyFromIndex(760)).thenReturn(196.075);
+        Mockito.when(math.getStopFrequencyFromIndex(767)).thenReturn(196.125);
+
+        BitSet assigned = new BitSet(768);
+        assigned.set(0, 8);
+        assigned.set(32, 40);
+        assigned.set(760, 768);
+
+        Available used = Mockito.mock(Available.class);
+        Mockito.when(used.assignedFrequencies()).thenReturn(assigned);
+
+        Map<Double, Double> expected = Map.of(191.325, 191.375, 191.525, 191.575, 196.075, 196.125);
+
+        Numeric frequencyService = new NumericFrequency(191.325, 768, math);
+        Assert.assertEquals(expected, frequencyService.assignedFrequency(used));
+
+    }
+
+    @Test
+    void availableFrequency() {
+        Math math = Mockito.mock(Math.class);
+        Mockito.when(math.getStartFrequencyFromIndex(760)).thenReturn(196.075);
+
+        Numeric frequencyService = new NumericFrequency(191.325, 768, math);
+
+        BitSet available = new BitSet(768);
+        available.set(0, 760);
+
+        Available bitMap = Mockito.mock(Available.class);
+        Mockito.when(bitMap.availableFrequencies()).thenReturn(available);
+
+        Map<Double, Double> expected = Map.of(191.325, 196.075);
+        Assert.assertEquals(expected, frequencyService.availableFrequency(bitMap));
+    }
+
+    @Test
+    void availableSplitByAssignedMiddleFrequencyRanges() {
+        Math math = Mockito.mock(Math.class);
+        Mockito.when(math.getStartFrequencyFromIndex(32)).thenReturn(191.525);
+        Mockito.when(math.getStartFrequencyFromIndex(40)).thenReturn(191.575);
+        Mockito.when(math.getStopFrequencyFromIndex(767)).thenReturn(196.125);
+
+        BitSet available = new BitSet(768);
+        available.set(0, 32);
+        available.set(40, 768);
+
+        Available used = Mockito.mock(Available.class);
+        Mockito.when(used.availableFrequencies()).thenReturn(available);
+
+        Map<Double, Double> expected = Map.of(191.325, 191.525, 191.575, 196.125);
+
+        Numeric frequencyService = new NumericFrequency(191.325, 768, math);
+        Assert.assertEquals(expected, frequencyService.availableFrequency(used));
+
+    }
+
+    @Test
+    void flexGrid() {
+        Math math = Mockito.mock(Math.class);
+        Mockito.when(math.getStartFrequencyFromIndex(5)).thenReturn(191.35625);
+        Mockito.when(math.getStartFrequencyFromIndex(11)).thenReturn(191.39374999999998);
+        Mockito.when(math.getStartFrequencyFromIndex(19)).thenReturn(191.44375);
+        Mockito.when(math.getStopFrequencyFromIndex(767)).thenReturn(196.125);
+
+        BitSet available = new BitSet(768);
+        available.set(5, 11);
+        available.set(19, 768);
+
+        Available used = Mockito.mock(Available.class);
+        Mockito.when(used.availableFrequencies()).thenReturn(available);
+
+        Map<Double, Double> expected = Map.of(191.35625, 191.39374999999998, 191.44375, 196.125);
+
+        Numeric frequencyService = new NumericFrequency(191.325, 768, math);
+        Assert.assertEquals(expected, frequencyService.availableFrequency(used));
+
+    }
+
+}
\ No newline at end of file