You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2022/07/05 18:13:51 UTC

[commons-lang] branch master updated: Add ArrayUtils.oneHot().

This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-lang.git


The following commit(s) were added to refs/heads/master by this push:
     new dad064eeb Add ArrayUtils.oneHot().
dad064eeb is described below

commit dad064eebbde450cb500a0e0d72200c655ac47c6
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Tue Jul 5 14:02:42 2022 -0400

    Add ArrayUtils.oneHot().
---
 src/changes/changes.xml                            |   1 +
 .../org/apache/commons/lang3/BooleanUtils.java     |  64 ++++++++++-
 .../org/apache/commons/lang3/BooleanUtilsTest.java | 119 ++++++++++++++++++++-
 3 files changed, 179 insertions(+), 5 deletions(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index b81f2ac9b..26fe8b46e 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -154,6 +154,7 @@ The <action> type attribute can be add,update,fix,remove.
     <action                   type="add" dev="ggregory" due-to="Gary Gregory">Add SystemUtils.IS_JAVA_16.</action>
     <action                   type="add" dev="ggregory" due-to="Gary Gregory">Add SystemUtils.IS_JAVA_17.</action>
     <action                   type="add" dev="ggregory" due-to="Gary Gregory">Add SystemUtils.IS_JAVA_18.</action>
+    <action issue="LANG-1627" type="add" dev="ggregory" due-to="Alberto Scotto, Avijit Chakraborty, Steve Bosman, Bruno P. Kinoshita, Gary Gregory">Add ArrayUtils.oneHot().</action>
     <!-- UPDATE -->
     <action                   type="update" dev="ggregory" due-to="Dependabot, XenoAmess, Gary Gregory">Bump actions/cache from 2.1.4 to 3.0.4 #742, #752, #764, #833, #867.</action>
     <action                   type="update" dev="ggregory" due-to="Dependabot">Bump actions/checkout from 2 to 3 #819, #825, #859.</action>
diff --git a/src/main/java/org/apache/commons/lang3/BooleanUtils.java b/src/main/java/org/apache/commons/lang3/BooleanUtils.java
index a33ff893b..bdf967c5d 100644
--- a/src/main/java/org/apache/commons/lang3/BooleanUtils.java
+++ b/src/main/java/org/apache/commons/lang3/BooleanUtils.java
@@ -247,6 +247,54 @@ public class BooleanUtils {
         }
         return bool.booleanValue() ? Boolean.FALSE : Boolean.TRUE;
     }
+    /**
+     * <p>Performs a one-hot on an array of booleans.</p>
+     * <p>
+     * This implementation returns true if one, and only one, of the supplied values is true.
+     * </p>
+     * <p>
+     * See also <a href="https://en.wikipedia.org/wiki/One-hot">One-hot</a>.
+     * </p>
+     * @param array  an array of {@code boolean}s
+     * @return the result of the one-hot operations
+     * @throws NullPointerException if {@code array} is {@code null}
+     * @throws IllegalArgumentException if {@code array} is empty.
+     */
+    public static boolean oneHot(final boolean... array) {
+        ObjectUtils.requireNonEmpty(array, "array");
+        boolean result = false;
+        for (boolean element: array) {
+            if (element) {
+                if (result) {
+                    return false;
+                }
+                result = element;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * <p>Performs a one-hot on an array of booleans.</p>
+     * <p>
+     * This implementation returns true if one, and only one, of the supplied values is true.
+     * </p>
+     * <p>
+     * Null array elements map to false, like {@code Boolean.parseBoolean(null)} and its callers return false.
+     * </p>
+     * <p>
+     * See also <a href="https://en.wikipedia.org/wiki/One-hot">One-hot</a>.
+     * </p>
+     *
+     * @param array  an array of {@code boolean}s
+     * @return the result of the one-hot operations
+     * @throws NullPointerException if {@code array} is {@code null}
+     * @throws IllegalArgumentException if {@code array} is empty.
+     */
+    public static Boolean oneHot(final Boolean... array) {
+        return Boolean.valueOf(oneHot(ArrayUtils.toPrimitive(array)));
+    }
+
     /**
      * <p>Performs an 'or' operation on a set of booleans.</p>
      *
@@ -1075,15 +1123,23 @@ public class BooleanUtils {
 
     /**
      * <p>Performs an xor on a set of booleans.</p>
+     * <p>
+     *   This behaves like an XOR gate;
+     *   it returns true if the number of true values is odd,
+     *   and false if the number of true values is zero or even.
+     * </p>
      *
      * <pre>
-     *   BooleanUtils.xor(true, true)   = false
-     *   BooleanUtils.xor(false, false) = false
-     *   BooleanUtils.xor(true, false)  = true
+     *   BooleanUtils.xor(true, true)             = false
+     *   BooleanUtils.xor(false, false)           = false
+     *   BooleanUtils.xor(true, false)            = true
+     *   BooleanUtils.xor(true, false, false)     = true
+     *   BooleanUtils.xor(true, true, true)       = true
+     *   BooleanUtils.xor(true, true, true, true) = false
      * </pre>
      *
      * @param array  an array of {@code boolean}s
-     * @return the result of the xor operations
+     * @return true if the number of true values in the array is odd; otherwise returns false.
      * @throws NullPointerException if {@code array} is {@code null}
      * @throws IllegalArgumentException if {@code array} is empty.
      */
diff --git a/src/test/java/org/apache/commons/lang3/BooleanUtilsTest.java b/src/test/java/org/apache/commons/lang3/BooleanUtilsTest.java
index a651b9002..d01579bd5 100644
--- a/src/test/java/org/apache/commons/lang3/BooleanUtilsTest.java
+++ b/src/test/java/org/apache/commons/lang3/BooleanUtilsTest.java
@@ -628,6 +628,124 @@ public class BooleanUtilsTest extends AbstractLangTest {
         assertFalse(Modifier.isFinal(BooleanUtils.class.getModifiers()));
     }
 
+    @Test
+    public void testOneHot_object_emptyInput() {
+        assertThrows(IllegalArgumentException.class, () -> BooleanUtils.oneHot(new Boolean[] {}));
+    }
+
+    @Test
+    public void testOneHot_object_nullElementInput() {
+        assertEquals(Boolean.FALSE, BooleanUtils.oneHot(new Boolean[] {null}));
+    }
+
+    @Test
+    public void testOneHot_object_nullInput() {
+        assertThrows(NullPointerException.class, () -> BooleanUtils.oneHot((Boolean[]) null));
+    }
+
+    @Test
+    public void testOneHot_object_validInput_1item() {
+        assertTrue(BooleanUtils.oneHot(new Boolean[]{Boolean.TRUE}), "true");
+
+        assertFalse(BooleanUtils.oneHot(new Boolean[]{Boolean.FALSE}), "false");
+
+        assertFalse(BooleanUtils.oneHot(new Boolean[]{null}), "false");
+    }
+
+    @Test
+    public void testOneHot_object_validInput_2items() {
+        assertFalse(BooleanUtils.oneHot(new Boolean[]{true, true}), "both true");
+
+        assertFalse(BooleanUtils.oneHot(new Boolean[]{false, false}), "both false");
+
+        assertTrue(BooleanUtils.oneHot(new Boolean[]{true, false}), "first true");
+
+        assertTrue(BooleanUtils.oneHot(new Boolean[]{false, true}), "last true");
+    }
+
+    @Test
+    public void testOneHot_object_validInput_2ItemsNullsTreatedAsFalse() {
+        assertFalse(BooleanUtils.oneHot(null, null), "both null");
+
+        assertTrue(BooleanUtils.oneHot(true, null), "first true");
+
+        assertTrue(BooleanUtils.oneHot(null, true), "last true");
+    }
+
+    @Test
+    public void testOneHot_object_validInput_3items() {
+        // none true
+        assertFalse(BooleanUtils.oneHot(new Boolean[]{false, false, false}), "all false");
+
+        // one true
+        assertTrue(BooleanUtils.oneHot(new Boolean[]{true, false, false}), "first true");
+
+        assertTrue(BooleanUtils.oneHot(new Boolean[]{false, true, false}), "middle true");
+
+        assertTrue(BooleanUtils.oneHot(new Boolean[]{false, false, true}), "last true");
+
+        // two true
+        assertFalse(BooleanUtils.oneHot(new Boolean[]{false, true, true}), "first false");
+
+        assertFalse(BooleanUtils.oneHot(new Boolean[]{true, false, true}), "middle false");
+
+        assertFalse(BooleanUtils.oneHot(new Boolean[]{true, true, false}), "last false");
+
+        // three true
+        assertFalse(BooleanUtils.oneHot(new Boolean[]{true, true, true}), "all true");
+    }
+
+    @Test
+    public void testOneHot_primitive_emptyInput() {
+        assertThrows(IllegalArgumentException.class, () -> BooleanUtils.oneHot(new boolean[] {}));
+    }
+
+    @Test
+    public void testOneHot_primitive_nullInput() {
+        assertThrows(NullPointerException.class, () -> BooleanUtils.oneHot((boolean[]) null));
+    }
+
+    @Test
+    public void testOneHot_primitive_validInput_1item() {
+        assertTrue(BooleanUtils.oneHot(new boolean[]{true}), "true");
+
+        assertFalse(BooleanUtils.oneHot(new boolean[]{false}), "false");
+    }
+
+    @Test
+    public void testOneHot_primitive_validInput_2items() {
+        assertFalse(BooleanUtils.oneHot(new boolean[]{true, true}), "both true");
+
+        assertFalse(BooleanUtils.oneHot(new boolean[]{false, false}), "both false");
+
+        assertTrue(BooleanUtils.oneHot(new boolean[]{true, false}), "first true");
+
+        assertTrue(BooleanUtils.oneHot(new boolean[]{false, true}), "last true");
+    }
+
+    @Test
+    public void testOneHot_primitive_validInput_3items() {
+        // none true
+        assertFalse(BooleanUtils.oneHot(new boolean[]{false, false, false}), "all false");
+
+        // one true
+        assertTrue(BooleanUtils.oneHot(new boolean[]{true, false, false}), "first true");
+
+        assertTrue(BooleanUtils.oneHot(new boolean[]{false, true, false}), "middle true");
+
+        assertTrue(BooleanUtils.oneHot(new boolean[]{false, false, true}), "last true");
+
+        // two true
+        assertFalse(BooleanUtils.oneHot(new boolean[]{false, true, true}), "first false");
+
+        assertFalse(BooleanUtils.oneHot(new boolean[]{true, false, true}), "middle false");
+
+        assertFalse(BooleanUtils.oneHot(new boolean[]{true, true, false}), "last false");
+
+        // three true
+        assertFalse(BooleanUtils.oneHot(new boolean[]{true, true, true}), "all true");
+    }
+
     @Test
     public void testOr_object_emptyInput() {
         assertThrows(IllegalArgumentException.class, () -> BooleanUtils.or(new Boolean[] {}));
@@ -1034,5 +1152,4 @@ public class BooleanUtilsTest extends AbstractLangTest {
                 BooleanUtils.xor(new boolean[] { true, true, true }),
                 "true ^ true ^ true");
     }
-
 }