Added sources from apache commons lang
This commit is contained in:
commit
dfcf4e96b9
11 changed files with 5689 additions and 0 deletions
32
.gitignore
vendored
Normal file
32
.gitignore
vendored
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
target/
|
||||||
|
!.mvn/wrapper/maven-wrapper.jar
|
||||||
|
!**/src/main/**/target/
|
||||||
|
!**/src/test/**/target/
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
### Eclipse ###
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
.sts4-cache
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
/nbproject/private/
|
||||||
|
/nbbuild/
|
||||||
|
/dist/
|
||||||
|
/nbdist/
|
||||||
|
/.nb-gradle/
|
||||||
|
build/
|
||||||
|
!**/src/main/**/build/
|
||||||
|
!**/src/test/**/build/
|
||||||
|
|
||||||
|
### VS Code ###
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
### Mac OS ###
|
||||||
|
.DS_Store
|
10
README.md
Normal file
10
README.md
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
# SDM assigment 04
|
||||||
|
|
||||||
|
Classes come from [https://github.com/apache/commons-lang](https://github.com/apache/commons-lang)
|
||||||
|
revision `770e72d2f78361b14f3fe27caea41e5977d3c638`
|
||||||
|
|
||||||
|
## Run Tests
|
||||||
|
|
||||||
|
```
|
||||||
|
mvn clean test
|
||||||
|
```
|
58
pom.xml
Normal file
58
pom.xml
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>org.example</groupId>
|
||||||
|
<artifactId>sdm04</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>11</maven.compiler.source>
|
||||||
|
<maven.compiler.target>11</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-lang3</artifactId>
|
||||||
|
<version>3.12.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.assertj</groupId>
|
||||||
|
<artifactId>assertj-core</artifactId>
|
||||||
|
<version>3.23.1</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
|
<version>5.9.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<version>3.10.1</version>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<version>2.19.1</version>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.platform</groupId>
|
||||||
|
<artifactId>junit-platform-surefire-provider</artifactId>
|
||||||
|
<version>1.1.0</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
379
src/main/java/ch/usi/inf/sdm/sdm04/CharRange.java
Normal file
379
src/main/java/ch/usi/inf/sdm/sdm04/CharRange.java
Normal file
|
@ -0,0 +1,379 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package ch.usi.inf.sdm.sdm04;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A contiguous range of characters, optionally negated.
|
||||||
|
*
|
||||||
|
* <p>Instances are immutable.</p>
|
||||||
|
*
|
||||||
|
* <p>#ThreadSafe#</p>
|
||||||
|
*
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
// TODO: This is no longer public and will be removed later as CharSet is moved
|
||||||
|
// to depend on Range.
|
||||||
|
final class CharRange implements Iterable<Character>, Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Required for serialization support. Lang version 2.0.
|
||||||
|
*
|
||||||
|
* @see Serializable
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 8270183163158333422L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The first character, inclusive, in the range.
|
||||||
|
*/
|
||||||
|
private final char start;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The last character, inclusive, in the range.
|
||||||
|
*/
|
||||||
|
private final char end;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if the range is everything except the characters specified.
|
||||||
|
*/
|
||||||
|
private final boolean negated;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cached toString.
|
||||||
|
*/
|
||||||
|
private transient String iToString;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Empty array.
|
||||||
|
*/
|
||||||
|
static final CharRange[] EMPTY_ARRAY = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a {@link CharRange} over a set of characters,
|
||||||
|
* optionally negating the range.
|
||||||
|
*
|
||||||
|
* <p>A negated range includes everything except that defined by the
|
||||||
|
* start and end characters.</p>
|
||||||
|
*
|
||||||
|
* <p>If start and end are in the wrong order, they are reversed.
|
||||||
|
* Thus {@code a-e} is the same as {@code e-a}.</p>
|
||||||
|
*
|
||||||
|
* @param start first character, inclusive, in this range
|
||||||
|
* @param end last character, inclusive, in this range
|
||||||
|
* @param negated true to express everything except the range
|
||||||
|
*/
|
||||||
|
private CharRange(char start, char end, final boolean negated) {
|
||||||
|
if (start > end) {
|
||||||
|
final char temp = start;
|
||||||
|
start = end;
|
||||||
|
end = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.start = start;
|
||||||
|
this.end = end;
|
||||||
|
this.negated = negated;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a {@link CharRange} over a single character.
|
||||||
|
*
|
||||||
|
* @param ch only character in this range
|
||||||
|
* @return the new CharRange object
|
||||||
|
* @since 2.5
|
||||||
|
*/
|
||||||
|
public static CharRange is(final char ch) {
|
||||||
|
return new CharRange(ch, ch, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a negated {@link CharRange} over a single character.
|
||||||
|
*
|
||||||
|
* <p>A negated range includes everything except that defined by the
|
||||||
|
* single character.</p>
|
||||||
|
*
|
||||||
|
* @param ch only character in this range
|
||||||
|
* @return the new CharRange object
|
||||||
|
* @since 2.5
|
||||||
|
*/
|
||||||
|
public static CharRange isNot(final char ch) {
|
||||||
|
return new CharRange(ch, ch, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a {@link CharRange} over a set of characters.
|
||||||
|
*
|
||||||
|
* <p>If start and end are in the wrong order, they are reversed.
|
||||||
|
* Thus {@code a-e} is the same as {@code e-a}.</p>
|
||||||
|
*
|
||||||
|
* @param start first character, inclusive, in this range
|
||||||
|
* @param end last character, inclusive, in this range
|
||||||
|
* @return the new CharRange object
|
||||||
|
* @since 2.5
|
||||||
|
*/
|
||||||
|
public static CharRange isIn(final char start, final char end) {
|
||||||
|
return new CharRange(start, end, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a negated {@link CharRange} over a set of characters.
|
||||||
|
*
|
||||||
|
* <p>A negated range includes everything except that defined by the
|
||||||
|
* start and end characters.</p>
|
||||||
|
*
|
||||||
|
* <p>If start and end are in the wrong order, they are reversed.
|
||||||
|
* Thus {@code a-e} is the same as {@code e-a}.</p>
|
||||||
|
*
|
||||||
|
* @param start first character, inclusive, in this range
|
||||||
|
* @param end last character, inclusive, in this range
|
||||||
|
* @return the new CharRange object
|
||||||
|
* @since 2.5
|
||||||
|
*/
|
||||||
|
public static CharRange isNotIn(final char start, final char end) {
|
||||||
|
return new CharRange(start, end, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accessors
|
||||||
|
/**
|
||||||
|
* Gets the start character for this character range.
|
||||||
|
*
|
||||||
|
* @return the start char (inclusive)
|
||||||
|
*/
|
||||||
|
public char getStart() {
|
||||||
|
return this.start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the end character for this character range.
|
||||||
|
*
|
||||||
|
* @return the end char (inclusive)
|
||||||
|
*/
|
||||||
|
public char getEnd() {
|
||||||
|
return this.end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this {@link CharRange} negated.
|
||||||
|
*
|
||||||
|
* <p>A negated range includes everything except that defined by the
|
||||||
|
* start and end characters.</p>
|
||||||
|
*
|
||||||
|
* @return {@code true} if negated
|
||||||
|
*/
|
||||||
|
public boolean isNegated() {
|
||||||
|
return negated;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contains
|
||||||
|
/**
|
||||||
|
* Is the character specified contained in this range.
|
||||||
|
*
|
||||||
|
* @param ch the character to check
|
||||||
|
* @return {@code true} if this range contains the input character
|
||||||
|
*/
|
||||||
|
public boolean contains(final char ch) {
|
||||||
|
return (ch >= start && ch <= end) != negated;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Are all the characters of the passed in range contained in
|
||||||
|
* this range.
|
||||||
|
*
|
||||||
|
* @param range the range to check against
|
||||||
|
* @return {@code true} if this range entirely contains the input range
|
||||||
|
* @throws NullPointerException if {@code null} input
|
||||||
|
*/
|
||||||
|
public boolean contains(final CharRange range) {
|
||||||
|
Objects.requireNonNull(range, "range");
|
||||||
|
if (negated) {
|
||||||
|
if (range.negated) {
|
||||||
|
return start >= range.start && end <= range.end;
|
||||||
|
}
|
||||||
|
return range.end < start || range.start > end;
|
||||||
|
}
|
||||||
|
if (range.negated) {
|
||||||
|
return start == 0 && end == Character.MAX_VALUE;
|
||||||
|
}
|
||||||
|
return start <= range.start && end >= range.end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basics
|
||||||
|
/**
|
||||||
|
* Compares two CharRange objects, returning true if they represent
|
||||||
|
* exactly the same range of characters defined in the same way.
|
||||||
|
*
|
||||||
|
* @param obj the object to compare to
|
||||||
|
* @return true if equal
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object obj) {
|
||||||
|
if (obj == this) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(obj instanceof CharRange)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final CharRange other = (CharRange) obj;
|
||||||
|
return start == other.start && end == other.end && negated == other.negated;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a hashCode compatible with the equals method.
|
||||||
|
*
|
||||||
|
* @return a suitable hashCode
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return 83 + start + 7 * end + (negated ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a string representation of the character range.
|
||||||
|
*
|
||||||
|
* @return string representation of this range
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (iToString == null) {
|
||||||
|
final StringBuilder buf = new StringBuilder(4);
|
||||||
|
if (isNegated()) {
|
||||||
|
buf.append('^');
|
||||||
|
}
|
||||||
|
buf.append(start);
|
||||||
|
if (start != end) {
|
||||||
|
buf.append('-');
|
||||||
|
buf.append(end);
|
||||||
|
}
|
||||||
|
iToString = buf.toString();
|
||||||
|
}
|
||||||
|
return iToString;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an iterator which can be used to walk through the characters described by this range.
|
||||||
|
*
|
||||||
|
* <p>#NotThreadSafe# the iterator is not thread-safe</p>
|
||||||
|
*
|
||||||
|
* @return an iterator to the chars represented by this range
|
||||||
|
* @since 2.5
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Iterator<Character> iterator() {
|
||||||
|
return new CharacterIterator(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Character {@link Iterator}.
|
||||||
|
* <p>#NotThreadSafe#</p>
|
||||||
|
*/
|
||||||
|
private static class CharacterIterator implements Iterator<Character> {
|
||||||
|
/**
|
||||||
|
* The current character
|
||||||
|
*/
|
||||||
|
private char current;
|
||||||
|
|
||||||
|
private final CharRange range;
|
||||||
|
private boolean hasNext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new iterator for the character range.
|
||||||
|
*
|
||||||
|
* @param r The character range
|
||||||
|
*/
|
||||||
|
private CharacterIterator(final CharRange r) {
|
||||||
|
range = r;
|
||||||
|
hasNext = true;
|
||||||
|
|
||||||
|
if (range.negated) {
|
||||||
|
if (range.start == 0) {
|
||||||
|
if (range.end == Character.MAX_VALUE) {
|
||||||
|
// This range is an empty set
|
||||||
|
hasNext = false;
|
||||||
|
} else {
|
||||||
|
current = (char) (range.end + 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
current = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
current = range.start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepares the next character in the range.
|
||||||
|
*/
|
||||||
|
private void prepareNext() {
|
||||||
|
if (range.negated) {
|
||||||
|
if (current == Character.MAX_VALUE) {
|
||||||
|
hasNext = false;
|
||||||
|
} else if (current + 1 == range.start) {
|
||||||
|
if (range.end == Character.MAX_VALUE) {
|
||||||
|
hasNext = false;
|
||||||
|
} else {
|
||||||
|
current = (char) (range.end + 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
current = (char) (current + 1);
|
||||||
|
}
|
||||||
|
} else if (current < range.end) {
|
||||||
|
current = (char) (current + 1);
|
||||||
|
} else {
|
||||||
|
hasNext = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Has the iterator not reached the end character yet?
|
||||||
|
*
|
||||||
|
* @return {@code true} if the iterator has yet to reach the character date
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return hasNext;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the next character in the iteration
|
||||||
|
*
|
||||||
|
* @return {@link Character} for the next character
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Character next() {
|
||||||
|
if (!hasNext) {
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
final char cur = current;
|
||||||
|
prepareNext();
|
||||||
|
return Character.valueOf(cur);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Always throws UnsupportedOperationException.
|
||||||
|
*
|
||||||
|
* @throws UnsupportedOperationException Always thrown.
|
||||||
|
* @see Iterator#remove()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
911
src/main/java/ch/usi/inf/sdm/sdm04/math/Fraction.java
Normal file
911
src/main/java/ch/usi/inf/sdm/sdm04/math/Fraction.java
Normal file
|
@ -0,0 +1,911 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package ch.usi.inf.sdm.sdm04.math;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Fraction} is a {@link Number} implementation that
|
||||||
|
* stores fractions accurately.
|
||||||
|
*
|
||||||
|
* <p>This class is immutable, and interoperable with most methods that accept
|
||||||
|
* a {@link Number}.</p>
|
||||||
|
*
|
||||||
|
* <p>Note that this class is intended for common use cases, it is <i>int</i>
|
||||||
|
* based and thus suffers from various overflow issues. For a BigInteger based
|
||||||
|
* equivalent, please see the Commons Math BigFraction class.</p>
|
||||||
|
*
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public final class Fraction extends Number implements Comparable<Fraction> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Required for serialization support. Lang version 2.0.
|
||||||
|
*
|
||||||
|
* @see java.io.Serializable
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 65382027393090L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Fraction} representation of 0.
|
||||||
|
*/
|
||||||
|
public static final Fraction ZERO = new Fraction(0, 1);
|
||||||
|
/**
|
||||||
|
* {@link Fraction} representation of 1.
|
||||||
|
*/
|
||||||
|
public static final Fraction ONE = new Fraction(1, 1);
|
||||||
|
/**
|
||||||
|
* {@link Fraction} representation of 1/2.
|
||||||
|
*/
|
||||||
|
public static final Fraction ONE_HALF = new Fraction(1, 2);
|
||||||
|
/**
|
||||||
|
* {@link Fraction} representation of 1/3.
|
||||||
|
*/
|
||||||
|
public static final Fraction ONE_THIRD = new Fraction(1, 3);
|
||||||
|
/**
|
||||||
|
* {@link Fraction} representation of 2/3.
|
||||||
|
*/
|
||||||
|
public static final Fraction TWO_THIRDS = new Fraction(2, 3);
|
||||||
|
/**
|
||||||
|
* {@link Fraction} representation of 1/4.
|
||||||
|
*/
|
||||||
|
public static final Fraction ONE_QUARTER = new Fraction(1, 4);
|
||||||
|
/**
|
||||||
|
* {@link Fraction} representation of 2/4.
|
||||||
|
*/
|
||||||
|
public static final Fraction TWO_QUARTERS = new Fraction(2, 4);
|
||||||
|
/**
|
||||||
|
* {@link Fraction} representation of 3/4.
|
||||||
|
*/
|
||||||
|
public static final Fraction THREE_QUARTERS = new Fraction(3, 4);
|
||||||
|
/**
|
||||||
|
* {@link Fraction} representation of 1/5.
|
||||||
|
*/
|
||||||
|
public static final Fraction ONE_FIFTH = new Fraction(1, 5);
|
||||||
|
/**
|
||||||
|
* {@link Fraction} representation of 2/5.
|
||||||
|
*/
|
||||||
|
public static final Fraction TWO_FIFTHS = new Fraction(2, 5);
|
||||||
|
/**
|
||||||
|
* {@link Fraction} representation of 3/5.
|
||||||
|
*/
|
||||||
|
public static final Fraction THREE_FIFTHS = new Fraction(3, 5);
|
||||||
|
/**
|
||||||
|
* {@link Fraction} representation of 4/5.
|
||||||
|
*/
|
||||||
|
public static final Fraction FOUR_FIFTHS = new Fraction(4, 5);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The numerator number part of the fraction (the three in three sevenths).
|
||||||
|
*/
|
||||||
|
private final int numerator;
|
||||||
|
/**
|
||||||
|
* The denominator number part of the fraction (the seven in three sevenths).
|
||||||
|
*/
|
||||||
|
private final int denominator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cached output hashCode (class is immutable).
|
||||||
|
*/
|
||||||
|
private transient int hashCode;
|
||||||
|
/**
|
||||||
|
* Cached output toString (class is immutable).
|
||||||
|
*/
|
||||||
|
private transient String toString;
|
||||||
|
/**
|
||||||
|
* Cached output toProperString (class is immutable).
|
||||||
|
*/
|
||||||
|
private transient String toProperString;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a {@link Fraction} instance with the 2 parts
|
||||||
|
* of a fraction Y/Z.
|
||||||
|
*
|
||||||
|
* @param numerator the numerator, for example the three in 'three sevenths'
|
||||||
|
* @param denominator the denominator, for example the seven in 'three sevenths'
|
||||||
|
*/
|
||||||
|
private Fraction(final int numerator, final int denominator) {
|
||||||
|
this.numerator = numerator;
|
||||||
|
this.denominator = denominator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link Fraction} instance with the 2 parts
|
||||||
|
* of a fraction Y/Z.
|
||||||
|
*
|
||||||
|
* <p>Any negative signs are resolved to be on the numerator.</p>
|
||||||
|
*
|
||||||
|
* @param numerator the numerator, for example the three in 'three sevenths'
|
||||||
|
* @param denominator the denominator, for example the seven in 'three sevenths'
|
||||||
|
* @return a new fraction instance
|
||||||
|
* @throws ArithmeticException if the denominator is {@code zero}
|
||||||
|
* or the denominator is {@code negative} and the numerator is {@code Integer#MIN_VALUE}
|
||||||
|
*/
|
||||||
|
public static Fraction getFraction(int numerator, int denominator) {
|
||||||
|
if (denominator == 0) {
|
||||||
|
throw new ArithmeticException("The denominator must not be zero");
|
||||||
|
}
|
||||||
|
if (denominator < 0) {
|
||||||
|
if (numerator == Integer.MIN_VALUE || denominator == Integer.MIN_VALUE) {
|
||||||
|
throw new ArithmeticException("overflow: can't negate");
|
||||||
|
}
|
||||||
|
numerator = -numerator;
|
||||||
|
denominator = -denominator;
|
||||||
|
}
|
||||||
|
return new Fraction(numerator, denominator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link Fraction} instance with the 3 parts
|
||||||
|
* of a fraction X Y/Z.
|
||||||
|
*
|
||||||
|
* <p>The negative sign must be passed in on the whole number part.</p>
|
||||||
|
*
|
||||||
|
* @param whole the whole number, for example the one in 'one and three sevenths'
|
||||||
|
* @param numerator the numerator, for example the three in 'one and three sevenths'
|
||||||
|
* @param denominator the denominator, for example the seven in 'one and three sevenths'
|
||||||
|
* @return a new fraction instance
|
||||||
|
* @throws ArithmeticException if the denominator is {@code zero}
|
||||||
|
* @throws ArithmeticException if the denominator is negative
|
||||||
|
* @throws ArithmeticException if the numerator is negative
|
||||||
|
* @throws ArithmeticException if the resulting numerator exceeds
|
||||||
|
* {@code Integer.MAX_VALUE}
|
||||||
|
*/
|
||||||
|
public static Fraction getFraction(final int whole, final int numerator, final int denominator) {
|
||||||
|
if (denominator == 0) {
|
||||||
|
throw new ArithmeticException("The denominator must not be zero");
|
||||||
|
}
|
||||||
|
if (denominator < 0) {
|
||||||
|
throw new ArithmeticException("The denominator must not be negative");
|
||||||
|
}
|
||||||
|
if (numerator < 0) {
|
||||||
|
throw new ArithmeticException("The numerator must not be negative");
|
||||||
|
}
|
||||||
|
final long numeratorValue;
|
||||||
|
if (whole < 0) {
|
||||||
|
numeratorValue = whole * (long) denominator - numerator;
|
||||||
|
} else {
|
||||||
|
numeratorValue = whole * (long) denominator + numerator;
|
||||||
|
}
|
||||||
|
if (numeratorValue < Integer.MIN_VALUE || numeratorValue > Integer.MAX_VALUE) {
|
||||||
|
throw new ArithmeticException("Numerator too large to represent as an Integer.");
|
||||||
|
}
|
||||||
|
return new Fraction((int) numeratorValue, denominator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a reduced {@link Fraction} instance with the 2 parts
|
||||||
|
* of a fraction Y/Z.
|
||||||
|
*
|
||||||
|
* <p>For example, if the input parameters represent 2/4, then the created
|
||||||
|
* fraction will be 1/2.</p>
|
||||||
|
*
|
||||||
|
* <p>Any negative signs are resolved to be on the numerator.</p>
|
||||||
|
*
|
||||||
|
* @param numerator the numerator, for example the three in 'three sevenths'
|
||||||
|
* @param denominator the denominator, for example the seven in 'three sevenths'
|
||||||
|
* @return a new fraction instance, with the numerator and denominator reduced
|
||||||
|
* @throws ArithmeticException if the denominator is {@code zero}
|
||||||
|
*/
|
||||||
|
public static Fraction getReducedFraction(int numerator, int denominator) {
|
||||||
|
if (denominator == 0) {
|
||||||
|
throw new ArithmeticException("The denominator must not be zero");
|
||||||
|
}
|
||||||
|
if (numerator == 0) {
|
||||||
|
return ZERO; // normalize zero.
|
||||||
|
}
|
||||||
|
// allow 2^k/-2^31 as a valid fraction (where k>0)
|
||||||
|
if (denominator == Integer.MIN_VALUE && (numerator & 1) == 0) {
|
||||||
|
numerator /= 2;
|
||||||
|
denominator /= 2;
|
||||||
|
}
|
||||||
|
if (denominator < 0) {
|
||||||
|
if (numerator == Integer.MIN_VALUE || denominator == Integer.MIN_VALUE) {
|
||||||
|
throw new ArithmeticException("overflow: can't negate");
|
||||||
|
}
|
||||||
|
numerator = -numerator;
|
||||||
|
denominator = -denominator;
|
||||||
|
}
|
||||||
|
// simplify fraction.
|
||||||
|
final int gcd = greatestCommonDivisor(numerator, denominator);
|
||||||
|
numerator /= gcd;
|
||||||
|
denominator /= gcd;
|
||||||
|
return new Fraction(numerator, denominator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link Fraction} instance from a {@code double} value.
|
||||||
|
*
|
||||||
|
* <p>This method uses the <a href="https://web.archive.org/web/20210516065058/http%3A//archives.math.utk.edu/articles/atuyl/confrac/">
|
||||||
|
* continued fraction algorithm</a>, computing a maximum of
|
||||||
|
* 25 convergents and bounding the denominator by 10,000.</p>
|
||||||
|
*
|
||||||
|
* @param value the double value to convert
|
||||||
|
* @return a new fraction instance that is close to the value
|
||||||
|
* @throws ArithmeticException if {@code |value| > Integer.MAX_VALUE}
|
||||||
|
* or {@code value = NaN}
|
||||||
|
* @throws ArithmeticException if the calculated denominator is {@code zero}
|
||||||
|
* @throws ArithmeticException if the algorithm does not converge
|
||||||
|
*/
|
||||||
|
public static Fraction getFraction(double value) {
|
||||||
|
final int sign = value < 0 ? -1 : 1;
|
||||||
|
value = Math.abs(value);
|
||||||
|
if (value > Integer.MAX_VALUE || Double.isNaN(value)) {
|
||||||
|
throw new ArithmeticException("The value must not be greater than Integer.MAX_VALUE or NaN");
|
||||||
|
}
|
||||||
|
final int wholeNumber = (int) value;
|
||||||
|
value -= wholeNumber;
|
||||||
|
|
||||||
|
int numer0 = 0; // the pre-previous
|
||||||
|
int denom0 = 1; // the pre-previous
|
||||||
|
int numer1 = 1; // the previous
|
||||||
|
int denom1 = 0; // the previous
|
||||||
|
int numer2; // the current, setup in calculation
|
||||||
|
int denom2; // the current, setup in calculation
|
||||||
|
int a1 = (int) value;
|
||||||
|
int a2;
|
||||||
|
double x1 = 1;
|
||||||
|
double x2;
|
||||||
|
double y1 = value - a1;
|
||||||
|
double y2;
|
||||||
|
double delta1, delta2 = Double.MAX_VALUE;
|
||||||
|
double fraction;
|
||||||
|
int i = 1;
|
||||||
|
do {
|
||||||
|
delta1 = delta2;
|
||||||
|
a2 = (int) (x1 / y1);
|
||||||
|
x2 = y1;
|
||||||
|
y2 = x1 - a2 * y1;
|
||||||
|
numer2 = a1 * numer1 + numer0;
|
||||||
|
denom2 = a1 * denom1 + denom0;
|
||||||
|
fraction = (double) numer2 / (double) denom2;
|
||||||
|
delta2 = Math.abs(value - fraction);
|
||||||
|
a1 = a2;
|
||||||
|
x1 = x2;
|
||||||
|
y1 = y2;
|
||||||
|
numer0 = numer1;
|
||||||
|
denom0 = denom1;
|
||||||
|
numer1 = numer2;
|
||||||
|
denom1 = denom2;
|
||||||
|
i++;
|
||||||
|
} while (delta1 > delta2 && denom2 <= 10000 && denom2 > 0 && i < 25);
|
||||||
|
if (i == 25) {
|
||||||
|
throw new ArithmeticException("Unable to convert double to fraction");
|
||||||
|
}
|
||||||
|
return getReducedFraction((numer0 + wholeNumber * denom0) * sign, denom0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Fraction from a {@link String}.
|
||||||
|
*
|
||||||
|
* <p>The formats accepted are:</p>
|
||||||
|
*
|
||||||
|
* <ol>
|
||||||
|
* <li>{@code double} String containing a dot</li>
|
||||||
|
* <li>'X Y/Z'</li>
|
||||||
|
* <li>'Y/Z'</li>
|
||||||
|
* <li>'X' (a simple whole number)</li>
|
||||||
|
* </ol>
|
||||||
|
* <p>and a .</p>
|
||||||
|
*
|
||||||
|
* @param str the string to parse, must not be {@code null}
|
||||||
|
* @return the new {@link Fraction} instance
|
||||||
|
* @throws NullPointerException if the string is {@code null}
|
||||||
|
* @throws NumberFormatException if the number format is invalid
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static Fraction getFraction(String str) {
|
||||||
|
Objects.requireNonNull(str, "str");
|
||||||
|
// parse double format
|
||||||
|
int pos = str.indexOf('.');
|
||||||
|
if (pos >= 0) {
|
||||||
|
return getFraction(Double.parseDouble(str));
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse X Y/Z format
|
||||||
|
pos = str.indexOf(' ');
|
||||||
|
if (pos > 0) {
|
||||||
|
final int whole = Integer.parseInt(str.substring(0, pos));
|
||||||
|
str = str.substring(pos + 1);
|
||||||
|
pos = str.indexOf('/');
|
||||||
|
if (pos < 0) {
|
||||||
|
throw new NumberFormatException("The fraction could not be parsed as the format X Y/Z");
|
||||||
|
}
|
||||||
|
final int numer = Integer.parseInt(str.substring(0, pos));
|
||||||
|
final int denom = Integer.parseInt(str.substring(pos + 1));
|
||||||
|
return getFraction(whole, numer, denom);
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse Y/Z format
|
||||||
|
pos = str.indexOf('/');
|
||||||
|
if (pos < 0) {
|
||||||
|
// simple whole number
|
||||||
|
return getFraction(Integer.parseInt(str), 1);
|
||||||
|
}
|
||||||
|
final int numer = Integer.parseInt(str.substring(0, pos));
|
||||||
|
final int denom = Integer.parseInt(str.substring(pos + 1));
|
||||||
|
return getFraction(numer, denom);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the numerator part of the fraction.
|
||||||
|
*
|
||||||
|
* <p>This method may return a value greater than the denominator, an
|
||||||
|
* improper fraction, such as the seven in 7/4.</p>
|
||||||
|
*
|
||||||
|
* @return the numerator fraction part
|
||||||
|
*/
|
||||||
|
|
||||||
|
public int getNumerator() {
|
||||||
|
return numerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the denominator part of the fraction.
|
||||||
|
*
|
||||||
|
* @return the denominator fraction part
|
||||||
|
*/
|
||||||
|
public int getDenominator() {
|
||||||
|
return denominator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the proper numerator, always positive.
|
||||||
|
*
|
||||||
|
* <p>An improper fraction 7/4 can be resolved into a proper one, 1 3/4.
|
||||||
|
* This method returns the 3 from the proper fraction.</p>
|
||||||
|
*
|
||||||
|
* <p>If the fraction is negative such as -7/4, it can be resolved into
|
||||||
|
* -1 3/4, so this method returns the positive proper numerator, 3.</p>
|
||||||
|
*
|
||||||
|
* @return the numerator fraction part of a proper fraction, always positive
|
||||||
|
*/
|
||||||
|
public int getProperNumerator() {
|
||||||
|
return Math.abs(numerator % denominator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the proper whole part of the fraction.
|
||||||
|
*
|
||||||
|
* <p>An improper fraction 7/4 can be resolved into a proper one, 1 3/4.
|
||||||
|
* This method returns the 1 from the proper fraction.</p>
|
||||||
|
*
|
||||||
|
* <p>If the fraction is negative such as -7/4, it can be resolved into
|
||||||
|
* -1 3/4, so this method returns the positive whole part -1.</p>
|
||||||
|
*
|
||||||
|
* @return the whole fraction part of a proper fraction, that includes the sign
|
||||||
|
*/
|
||||||
|
public int getProperWhole() {
|
||||||
|
return numerator / denominator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the fraction as an {@code int}. This returns the whole number
|
||||||
|
* part of the fraction.
|
||||||
|
*
|
||||||
|
* @return the whole number fraction part
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int intValue() {
|
||||||
|
return numerator / denominator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the fraction as a {@code long}. This returns the whole number
|
||||||
|
* part of the fraction.
|
||||||
|
*
|
||||||
|
* @return the whole number fraction part
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public long longValue() {
|
||||||
|
return (long) numerator / denominator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the fraction as a {@code float}. This calculates the fraction
|
||||||
|
* as the numerator divided by denominator.
|
||||||
|
*
|
||||||
|
* @return the fraction as a {@code float}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public float floatValue() {
|
||||||
|
return (float) numerator / (float) denominator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the fraction as a {@code double}. This calculates the fraction
|
||||||
|
* as the numerator divided by denominator.
|
||||||
|
*
|
||||||
|
* @return the fraction as a {@code double}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public double doubleValue() {
|
||||||
|
return (double) numerator / (double) denominator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reduce the fraction to the smallest values for the numerator and
|
||||||
|
* denominator, returning the result.
|
||||||
|
*
|
||||||
|
* <p>For example, if this fraction represents 2/4, then the result
|
||||||
|
* will be 1/2.</p>
|
||||||
|
*
|
||||||
|
* @return a new reduced fraction instance, or this if no simplification possible
|
||||||
|
*/
|
||||||
|
public Fraction reduce() {
|
||||||
|
if (numerator == 0) {
|
||||||
|
return equals(ZERO) ? this : ZERO;
|
||||||
|
}
|
||||||
|
final int gcd = greatestCommonDivisor(Math.abs(numerator), denominator);
|
||||||
|
if (gcd == 1) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return getFraction(numerator / gcd, denominator / gcd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a fraction that is the inverse (1/fraction) of this one.
|
||||||
|
*
|
||||||
|
* <p>The returned fraction is not reduced.</p>
|
||||||
|
*
|
||||||
|
* @return a new fraction instance with the numerator and denominator
|
||||||
|
* inverted.
|
||||||
|
* @throws ArithmeticException if the fraction represents zero.
|
||||||
|
*/
|
||||||
|
public Fraction invert() {
|
||||||
|
if (numerator == 0) {
|
||||||
|
throw new ArithmeticException("Unable to invert zero.");
|
||||||
|
}
|
||||||
|
if (numerator==Integer.MIN_VALUE) {
|
||||||
|
throw new ArithmeticException("overflow: can't negate numerator");
|
||||||
|
}
|
||||||
|
if (numerator<0) {
|
||||||
|
return new Fraction(-denominator, -numerator);
|
||||||
|
}
|
||||||
|
return new Fraction(denominator, numerator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a fraction that is the negative (-fraction) of this one.
|
||||||
|
*
|
||||||
|
* <p>The returned fraction is not reduced.</p>
|
||||||
|
*
|
||||||
|
* @return a new fraction instance with the opposite signed numerator
|
||||||
|
*/
|
||||||
|
public Fraction negate() {
|
||||||
|
// the positive range is one smaller than the negative range of an int.
|
||||||
|
if (numerator==Integer.MIN_VALUE) {
|
||||||
|
throw new ArithmeticException("overflow: too large to negate");
|
||||||
|
}
|
||||||
|
return new Fraction(-numerator, denominator);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a fraction that is the positive equivalent of this one.
|
||||||
|
* <p>More precisely: {@code (fraction >= 0 ? this : -fraction)}</p>
|
||||||
|
*
|
||||||
|
* <p>The returned fraction is not reduced.</p>
|
||||||
|
*
|
||||||
|
* @return {@code this} if it is positive, or a new positive fraction
|
||||||
|
* instance with the opposite signed numerator
|
||||||
|
*/
|
||||||
|
public Fraction abs() {
|
||||||
|
if (numerator >= 0) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return negate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a fraction that is raised to the passed in power.
|
||||||
|
*
|
||||||
|
* <p>The returned fraction is in reduced form.</p>
|
||||||
|
*
|
||||||
|
* @param power the power to raise the fraction to
|
||||||
|
* @return {@code this} if the power is one, {@link #ONE} if the power
|
||||||
|
* is zero (even if the fraction equals ZERO) or a new fraction instance
|
||||||
|
* raised to the appropriate power
|
||||||
|
* @throws ArithmeticException if the resulting numerator or denominator exceeds
|
||||||
|
* {@code Integer.MAX_VALUE}
|
||||||
|
*/
|
||||||
|
public Fraction pow(final int power) {
|
||||||
|
if (power == 1) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
if (power == 0) {
|
||||||
|
return ONE;
|
||||||
|
}
|
||||||
|
if (power < 0) {
|
||||||
|
if (power == Integer.MIN_VALUE) { // MIN_VALUE can't be negated.
|
||||||
|
return this.invert().pow(2).pow(-(power / 2));
|
||||||
|
}
|
||||||
|
return this.invert().pow(-power);
|
||||||
|
}
|
||||||
|
final Fraction f = this.multiplyBy(this);
|
||||||
|
if (power % 2 == 0) { // if even...
|
||||||
|
return f.pow(power / 2);
|
||||||
|
}
|
||||||
|
return f.pow(power / 2).multiplyBy(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the greatest common divisor of the absolute value of
|
||||||
|
* two numbers, using the "binary gcd" method which avoids
|
||||||
|
* division and modulo operations. See Knuth 4.5.2 algorithm B.
|
||||||
|
* This algorithm is due to Josef Stein (1961).
|
||||||
|
*
|
||||||
|
* @param u a non-zero number
|
||||||
|
* @param v a non-zero number
|
||||||
|
* @return the greatest common divisor, never zero
|
||||||
|
*/
|
||||||
|
private static int greatestCommonDivisor(int u, int v) {
|
||||||
|
// From Commons Math:
|
||||||
|
if (u == 0 || v == 0) {
|
||||||
|
if (u == Integer.MIN_VALUE || v == Integer.MIN_VALUE) {
|
||||||
|
throw new ArithmeticException("overflow: gcd is 2^31");
|
||||||
|
}
|
||||||
|
return Math.abs(u) + Math.abs(v);
|
||||||
|
}
|
||||||
|
// if either operand is abs 1, return 1:
|
||||||
|
if (Math.abs(u) == 1 || Math.abs(v) == 1) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
// keep u and v negative, as negative integers range down to
|
||||||
|
// -2^31, while positive numbers can only be as large as 2^31-1
|
||||||
|
// (i.e. we can't necessarily negate a negative number without
|
||||||
|
// overflow)
|
||||||
|
if (u > 0) {
|
||||||
|
u = -u;
|
||||||
|
} // make u negative
|
||||||
|
if (v > 0) {
|
||||||
|
v = -v;
|
||||||
|
} // make v negative
|
||||||
|
// B1. [Find power of 2]
|
||||||
|
int k = 0;
|
||||||
|
while ((u & 1) == 0 && (v & 1) == 0 && k < 31) { // while u and v are both even...
|
||||||
|
u /= 2;
|
||||||
|
v /= 2;
|
||||||
|
k++; // cast out twos.
|
||||||
|
}
|
||||||
|
if (k == 31) {
|
||||||
|
throw new ArithmeticException("overflow: gcd is 2^31");
|
||||||
|
}
|
||||||
|
// B2. Initialize: u and v have been divided by 2^k and at least
|
||||||
|
// one is odd.
|
||||||
|
int t = (u & 1) == 1 ? v : -(u / 2)/* B3 */;
|
||||||
|
// t negative: u was odd, v may be even (t replaces v)
|
||||||
|
// t positive: u was even, v is odd (t replaces u)
|
||||||
|
do {
|
||||||
|
/* assert u<0 && v<0; */
|
||||||
|
// B4/B3: cast out twos from t.
|
||||||
|
while ((t & 1) == 0) { // while t is even.
|
||||||
|
t /= 2; // cast out twos
|
||||||
|
}
|
||||||
|
// B5 [reset max(u,v)]
|
||||||
|
if (t > 0) {
|
||||||
|
u = -t;
|
||||||
|
} else {
|
||||||
|
v = t;
|
||||||
|
}
|
||||||
|
// B6/B3. at this point both u and v should be odd.
|
||||||
|
t = (v - u) / 2;
|
||||||
|
// |u| larger: t positive (replace u)
|
||||||
|
// |v| larger: t negative (replace v)
|
||||||
|
} while (t != 0);
|
||||||
|
return -u * (1 << k); // gcd is u*2^k
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multiply two integers, checking for overflow.
|
||||||
|
*
|
||||||
|
* @param x a factor
|
||||||
|
* @param y a factor
|
||||||
|
* @return the product {@code x*y}
|
||||||
|
* @throws ArithmeticException if the result can not be represented as
|
||||||
|
* an int
|
||||||
|
*/
|
||||||
|
private static int mulAndCheck(final int x, final int y) {
|
||||||
|
final long m = (long) x * (long) y;
|
||||||
|
if (m < Integer.MIN_VALUE || m > Integer.MAX_VALUE) {
|
||||||
|
throw new ArithmeticException("overflow: mul");
|
||||||
|
}
|
||||||
|
return (int) m;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multiply two non-negative integers, checking for overflow.
|
||||||
|
*
|
||||||
|
* @param x a non-negative factor
|
||||||
|
* @param y a non-negative factor
|
||||||
|
* @return the product {@code x*y}
|
||||||
|
* @throws ArithmeticException if the result can not be represented as
|
||||||
|
* an int
|
||||||
|
*/
|
||||||
|
private static int mulPosAndCheck(final int x, final int y) {
|
||||||
|
/* assert x>=0 && y>=0; */
|
||||||
|
final long m = (long) x * (long) y;
|
||||||
|
if (m > Integer.MAX_VALUE) {
|
||||||
|
throw new ArithmeticException("overflow: mulPos");
|
||||||
|
}
|
||||||
|
return (int) m;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add two integers, checking for overflow.
|
||||||
|
*
|
||||||
|
* @param x an addend
|
||||||
|
* @param y an addend
|
||||||
|
* @return the sum {@code x+y}
|
||||||
|
* @throws ArithmeticException if the result can not be represented as
|
||||||
|
* an int
|
||||||
|
*/
|
||||||
|
private static int addAndCheck(final int x, final int y) {
|
||||||
|
final long s = (long) x + (long) y;
|
||||||
|
if (s < Integer.MIN_VALUE || s > Integer.MAX_VALUE) {
|
||||||
|
throw new ArithmeticException("overflow: add");
|
||||||
|
}
|
||||||
|
return (int) s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subtract two integers, checking for overflow.
|
||||||
|
*
|
||||||
|
* @param x the minuend
|
||||||
|
* @param y the subtrahend
|
||||||
|
* @return the difference {@code x-y}
|
||||||
|
* @throws ArithmeticException if the result can not be represented as
|
||||||
|
* an int
|
||||||
|
*/
|
||||||
|
private static int subAndCheck(final int x, final int y) {
|
||||||
|
final long s = (long) x - (long) y;
|
||||||
|
if (s < Integer.MIN_VALUE || s > Integer.MAX_VALUE) {
|
||||||
|
throw new ArithmeticException("overflow: add");
|
||||||
|
}
|
||||||
|
return (int) s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the value of this fraction to another, returning the result in reduced form.
|
||||||
|
* The algorithm follows Knuth, 4.5.1.
|
||||||
|
*
|
||||||
|
* @param fraction the fraction to add, must not be {@code null}
|
||||||
|
* @return a {@link Fraction} instance with the resulting values
|
||||||
|
* @throws IllegalArgumentException if the fraction is {@code null}
|
||||||
|
* @throws ArithmeticException if the resulting numerator or denominator exceeds
|
||||||
|
* {@code Integer.MAX_VALUE}
|
||||||
|
*/
|
||||||
|
public Fraction add(final Fraction fraction) {
|
||||||
|
return addSub(fraction, true /* add */);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subtracts the value of another fraction from the value of this one,
|
||||||
|
* returning the result in reduced form.
|
||||||
|
*
|
||||||
|
* @param fraction the fraction to subtract, must not be {@code null}
|
||||||
|
* @return a {@link Fraction} instance with the resulting values
|
||||||
|
* @throws IllegalArgumentException if the fraction is {@code null}
|
||||||
|
* @throws ArithmeticException if the resulting numerator or denominator
|
||||||
|
* cannot be represented in an {@code int}.
|
||||||
|
*/
|
||||||
|
public Fraction subtract(final Fraction fraction) {
|
||||||
|
return addSub(fraction, false /* subtract */);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implement add and subtract using algorithm described in Knuth 4.5.1.
|
||||||
|
*
|
||||||
|
* @param fraction the fraction to subtract, must not be {@code null}
|
||||||
|
* @param isAdd true to add, false to subtract
|
||||||
|
* @return a {@link Fraction} instance with the resulting values
|
||||||
|
* @throws IllegalArgumentException if the fraction is {@code null}
|
||||||
|
* @throws ArithmeticException if the resulting numerator or denominator
|
||||||
|
* cannot be represented in an {@code int}.
|
||||||
|
*/
|
||||||
|
private Fraction addSub(final Fraction fraction, final boolean isAdd) {
|
||||||
|
Objects.requireNonNull(fraction, "fraction");
|
||||||
|
// zero is identity for addition.
|
||||||
|
if (numerator == 0) {
|
||||||
|
return isAdd ? fraction : fraction.negate();
|
||||||
|
}
|
||||||
|
if (fraction.numerator == 0) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
// if denominators are randomly distributed, d1 will be 1 about 61%
|
||||||
|
// of the time.
|
||||||
|
final int d1 = greatestCommonDivisor(denominator, fraction.denominator);
|
||||||
|
if (d1 == 1) {
|
||||||
|
// result is ( (u*v' +/- u'v) / u'v')
|
||||||
|
final int uvp = mulAndCheck(numerator, fraction.denominator);
|
||||||
|
final int upv = mulAndCheck(fraction.numerator, denominator);
|
||||||
|
return new Fraction(isAdd ? addAndCheck(uvp, upv) : subAndCheck(uvp, upv), mulPosAndCheck(denominator,
|
||||||
|
fraction.denominator));
|
||||||
|
}
|
||||||
|
// the quantity 't' requires 65 bits of precision; see knuth 4.5.1
|
||||||
|
// exercise 7. we're going to use a BigInteger.
|
||||||
|
// t = u(v'/d1) +/- v(u'/d1)
|
||||||
|
final BigInteger uvp = BigInteger.valueOf(numerator).multiply(BigInteger.valueOf(fraction.denominator / d1));
|
||||||
|
final BigInteger upv = BigInteger.valueOf(fraction.numerator).multiply(BigInteger.valueOf(denominator / d1));
|
||||||
|
final BigInteger t = isAdd ? uvp.add(upv) : uvp.subtract(upv);
|
||||||
|
// but d2 doesn't need extra precision because
|
||||||
|
// d2 = gcd(t,d1) = gcd(t mod d1, d1)
|
||||||
|
final int tmodd1 = t.mod(BigInteger.valueOf(d1)).intValue();
|
||||||
|
final int d2 = tmodd1 == 0 ? d1 : greatestCommonDivisor(tmodd1, d1);
|
||||||
|
|
||||||
|
// result is (t/d2) / (u'/d1)(v'/d2)
|
||||||
|
final BigInteger w = t.divide(BigInteger.valueOf(d2));
|
||||||
|
if (w.bitLength() > 31) {
|
||||||
|
throw new ArithmeticException("overflow: numerator too large after multiply");
|
||||||
|
}
|
||||||
|
return new Fraction(w.intValue(), mulPosAndCheck(denominator / d1, fraction.denominator / d2));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multiplies the value of this fraction by another, returning the
|
||||||
|
* result in reduced form.
|
||||||
|
*
|
||||||
|
* @param fraction the fraction to multiply by, must not be {@code null}
|
||||||
|
* @return a {@link Fraction} instance with the resulting values
|
||||||
|
* @throws NullPointerException if the fraction is {@code null}
|
||||||
|
* @throws ArithmeticException if the resulting numerator or denominator exceeds
|
||||||
|
* {@code Integer.MAX_VALUE}
|
||||||
|
*/
|
||||||
|
public Fraction multiplyBy(final Fraction fraction) {
|
||||||
|
Objects.requireNonNull(fraction, "fraction");
|
||||||
|
if (numerator == 0 || fraction.numerator == 0) {
|
||||||
|
return ZERO;
|
||||||
|
}
|
||||||
|
// knuth 4.5.1
|
||||||
|
// make sure we don't overflow unless the result *must* overflow.
|
||||||
|
final int d1 = greatestCommonDivisor(numerator, fraction.denominator);
|
||||||
|
final int d2 = greatestCommonDivisor(fraction.numerator, denominator);
|
||||||
|
return getReducedFraction(mulAndCheck(numerator / d1, fraction.numerator / d2),
|
||||||
|
mulPosAndCheck(denominator / d2, fraction.denominator / d1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Divide the value of this fraction by another.
|
||||||
|
*
|
||||||
|
* @param fraction the fraction to divide by, must not be {@code null}
|
||||||
|
* @return a {@link Fraction} instance with the resulting values
|
||||||
|
* @throws NullPointerException if the fraction is {@code null}
|
||||||
|
* @throws ArithmeticException if the fraction to divide by is zero
|
||||||
|
* @throws ArithmeticException if the resulting numerator or denominator exceeds
|
||||||
|
* {@code Integer.MAX_VALUE}
|
||||||
|
*/
|
||||||
|
public Fraction divideBy(final Fraction fraction) {
|
||||||
|
Objects.requireNonNull(fraction, "fraction");
|
||||||
|
if (fraction.numerator == 0) {
|
||||||
|
throw new ArithmeticException("The fraction to divide by must not be zero");
|
||||||
|
}
|
||||||
|
return multiplyBy(fraction.invert());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares this fraction to another object to test if they are equal..
|
||||||
|
*
|
||||||
|
* <p>To be equal, both values must be equal. Thus 2/4 is not equal to 1/2.</p>
|
||||||
|
*
|
||||||
|
* @param obj the reference object with which to compare
|
||||||
|
* @return {@code true} if this object is equal
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object obj) {
|
||||||
|
if (obj == this) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(obj instanceof Fraction)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final Fraction other = (Fraction) obj;
|
||||||
|
return getNumerator() == other.getNumerator() && getDenominator() == other.getDenominator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a hashCode for the fraction.
|
||||||
|
*
|
||||||
|
* @return a hash code value for this object
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
if (hashCode == 0) {
|
||||||
|
// hash code update should be atomic.
|
||||||
|
hashCode = 37 * (37 * 17 + getNumerator()) + getDenominator();
|
||||||
|
}
|
||||||
|
return hashCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares this object to another based on size.
|
||||||
|
*
|
||||||
|
* <p>Note: this class has a natural ordering that is inconsistent
|
||||||
|
* with equals, because, for example, equals treats 1/2 and 2/4 as
|
||||||
|
* different, whereas compareTo treats them as equal.
|
||||||
|
*
|
||||||
|
* @param other the object to compare to
|
||||||
|
* @return -1 if this is less, 0 if equal, +1 if greater
|
||||||
|
* @throws ClassCastException if the object is not a {@link Fraction}
|
||||||
|
* @throws NullPointerException if the object is {@code null}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int compareTo(final Fraction other) {
|
||||||
|
if (this == other) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (numerator == other.numerator && denominator == other.denominator) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise see which is less
|
||||||
|
final long first = (long) numerator * (long) other.denominator;
|
||||||
|
final long second = (long) other.numerator * (long) denominator;
|
||||||
|
return Long.compare(first, second);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the fraction as a {@link String}.
|
||||||
|
*
|
||||||
|
* <p>The format used is '<i>numerator</i>/<i>denominator</i>' always.
|
||||||
|
*
|
||||||
|
* @return a {@link String} form of the fraction
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (toString == null) {
|
||||||
|
toString = getNumerator() + "/" + getDenominator();
|
||||||
|
}
|
||||||
|
return toString;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the fraction as a proper {@link String} in the format X Y/Z.
|
||||||
|
*
|
||||||
|
* <p>The format used in '<i>wholeNumber</i> <i>numerator</i>/<i>denominator</i>'.
|
||||||
|
* If the whole number is zero it will be omitted. If the numerator is zero,
|
||||||
|
* only the whole number is returned.</p>
|
||||||
|
*
|
||||||
|
* @return a {@link String} form of the fraction
|
||||||
|
*/
|
||||||
|
public String toProperString() {
|
||||||
|
if (toProperString == null) {
|
||||||
|
if (numerator == 0) {
|
||||||
|
toProperString = "0";
|
||||||
|
} else if (numerator == denominator) {
|
||||||
|
toProperString = "1";
|
||||||
|
} else if (numerator == -1 * denominator) {
|
||||||
|
toProperString = "-1";
|
||||||
|
} else if ((numerator > 0 ? -numerator : numerator) < -denominator) {
|
||||||
|
// note that we do the magnitude comparison test above with
|
||||||
|
// NEGATIVE (not positive) numbers, since negative numbers
|
||||||
|
// have a larger range. otherwise numerator==Integer.MIN_VALUE
|
||||||
|
// is handled incorrectly.
|
||||||
|
final int properNumerator = getProperNumerator();
|
||||||
|
if (properNumerator == 0) {
|
||||||
|
toProperString = Integer.toString(getProperWhole());
|
||||||
|
} else {
|
||||||
|
toProperString = getProperWhole() + " " + properNumerator + "/" + getDenominator();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
toProperString = getNumerator() + "/" + getDenominator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return toProperString;
|
||||||
|
}
|
||||||
|
}
|
252
src/main/java/ch/usi/inf/sdm/sdm04/math/IEEE754rUtils.java
Normal file
252
src/main/java/ch/usi/inf/sdm/sdm04/math/IEEE754rUtils.java
Normal file
|
@ -0,0 +1,252 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package ch.usi.inf.sdm.sdm04.math;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides IEEE-754r variants of NumberUtils methods.
|
||||||
|
*
|
||||||
|
* <p>See: <a href="https://en.wikipedia.org/wiki/IEEE_754r">https://en.wikipedia.org/wiki/IEEE_754r</a></p>
|
||||||
|
*
|
||||||
|
* @since 2.4
|
||||||
|
*/
|
||||||
|
public class IEEE754rUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the minimum value in an array.
|
||||||
|
*
|
||||||
|
* @param array an array, must not be null or empty
|
||||||
|
* @return the minimum value in the array
|
||||||
|
* @throws NullPointerException if {@code array} is {@code null}
|
||||||
|
* @throws IllegalArgumentException if {@code array} is empty
|
||||||
|
* @since 3.4 Changed signature from min(double[]) to min(double...)
|
||||||
|
*/
|
||||||
|
public static double min(final double... array) {
|
||||||
|
Objects.requireNonNull(array, "array");
|
||||||
|
Validate.isTrue(array.length != 0, "Array cannot be empty.");
|
||||||
|
|
||||||
|
// Finds and returns min
|
||||||
|
double min = array[0];
|
||||||
|
for (int i = 1; i < array.length; i++) {
|
||||||
|
min = min(array[i], min);
|
||||||
|
}
|
||||||
|
|
||||||
|
return min;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the minimum value in an array.
|
||||||
|
*
|
||||||
|
* @param array an array, must not be null or empty
|
||||||
|
* @return the minimum value in the array
|
||||||
|
* @throws NullPointerException if {@code array} is {@code null}
|
||||||
|
* @throws IllegalArgumentException if {@code array} is empty
|
||||||
|
* @since 3.4 Changed signature from min(float[]) to min(float...)
|
||||||
|
*/
|
||||||
|
public static float min(final float... array) {
|
||||||
|
Objects.requireNonNull(array, "array");
|
||||||
|
Validate.isTrue(array.length != 0, "Array cannot be empty.");
|
||||||
|
|
||||||
|
// Finds and returns min
|
||||||
|
float min = array[0];
|
||||||
|
for (int i = 1; i < array.length; i++) {
|
||||||
|
min = min(array[i], min);
|
||||||
|
}
|
||||||
|
|
||||||
|
return min;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the minimum of three {@code double} values.
|
||||||
|
*
|
||||||
|
* <p>NaN is only returned if all numbers are NaN as per IEEE-754r.</p>
|
||||||
|
*
|
||||||
|
* @param a value 1
|
||||||
|
* @param b value 2
|
||||||
|
* @param c value 3
|
||||||
|
* @return the smallest of the values
|
||||||
|
*/
|
||||||
|
public static double min(final double a, final double b, final double c) {
|
||||||
|
return min(min(a, b), c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the minimum of two {@code double} values.
|
||||||
|
*
|
||||||
|
* <p>NaN is only returned if all numbers are NaN as per IEEE-754r.</p>
|
||||||
|
*
|
||||||
|
* @param a value 1
|
||||||
|
* @param b value 2
|
||||||
|
* @return the smallest of the values
|
||||||
|
*/
|
||||||
|
public static double min(final double a, final double b) {
|
||||||
|
if (Double.isNaN(a)) {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
if (Double.isNaN(b)) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
return Math.min(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the minimum of three {@code float} values.
|
||||||
|
*
|
||||||
|
* <p>NaN is only returned if all numbers are NaN as per IEEE-754r.</p>
|
||||||
|
*
|
||||||
|
* @param a value 1
|
||||||
|
* @param b value 2
|
||||||
|
* @param c value 3
|
||||||
|
* @return the smallest of the values
|
||||||
|
*/
|
||||||
|
public static float min(final float a, final float b, final float c) {
|
||||||
|
return min(min(a, b), c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the minimum of two {@code float} values.
|
||||||
|
*
|
||||||
|
* <p>NaN is only returned if all numbers are NaN as per IEEE-754r.</p>
|
||||||
|
*
|
||||||
|
* @param a value 1
|
||||||
|
* @param b value 2
|
||||||
|
* @return the smallest of the values
|
||||||
|
*/
|
||||||
|
public static float min(final float a, final float b) {
|
||||||
|
if (Float.isNaN(a)) {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
if (Float.isNaN(b)) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
return Math.min(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the maximum value in an array.
|
||||||
|
*
|
||||||
|
* @param array an array, must not be null or empty
|
||||||
|
* @return the minimum value in the array
|
||||||
|
* @throws NullPointerException if {@code array} is {@code null}
|
||||||
|
* @throws IllegalArgumentException if {@code array} is empty
|
||||||
|
* @since 3.4 Changed signature from max(double[]) to max(double...)
|
||||||
|
*/
|
||||||
|
public static double max(final double... array) {
|
||||||
|
Objects.requireNonNull(array, "array");
|
||||||
|
Validate.isTrue(array.length != 0, "Array cannot be empty.");
|
||||||
|
|
||||||
|
// Finds and returns max
|
||||||
|
double max = array[0];
|
||||||
|
for (int j = 1; j < array.length; j++) {
|
||||||
|
max = max(array[j], max);
|
||||||
|
}
|
||||||
|
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the maximum value in an array.
|
||||||
|
*
|
||||||
|
* @param array an array, must not be null or empty
|
||||||
|
* @return the minimum value in the array
|
||||||
|
* @throws NullPointerException if {@code array} is {@code null}
|
||||||
|
* @throws IllegalArgumentException if {@code array} is empty
|
||||||
|
* @since 3.4 Changed signature from max(float[]) to max(float...)
|
||||||
|
*/
|
||||||
|
public static float max(final float... array) {
|
||||||
|
Objects.requireNonNull(array, "array");
|
||||||
|
Validate.isTrue(array.length != 0, "Array cannot be empty.");
|
||||||
|
|
||||||
|
// Finds and returns max
|
||||||
|
float max = array[0];
|
||||||
|
for (int j = 1; j < array.length; j++) {
|
||||||
|
max = max(array[j], max);
|
||||||
|
}
|
||||||
|
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the maximum of three {@code double} values.
|
||||||
|
*
|
||||||
|
* <p>NaN is only returned if all numbers are NaN as per IEEE-754r.</p>
|
||||||
|
*
|
||||||
|
* @param a value 1
|
||||||
|
* @param b value 2
|
||||||
|
* @param c value 3
|
||||||
|
* @return the largest of the values
|
||||||
|
*/
|
||||||
|
public static double max(final double a, final double b, final double c) {
|
||||||
|
return max(max(a, b), c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the maximum of two {@code double} values.
|
||||||
|
*
|
||||||
|
* <p>NaN is only returned if all numbers are NaN as per IEEE-754r.</p>
|
||||||
|
*
|
||||||
|
* @param a value 1
|
||||||
|
* @param b value 2
|
||||||
|
* @return the largest of the values
|
||||||
|
*/
|
||||||
|
public static double max(final double a, final double b) {
|
||||||
|
if (Double.isNaN(a)) {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
if (Double.isNaN(b)) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
return Math.max(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the maximum of three {@code float} values.
|
||||||
|
*
|
||||||
|
* <p>NaN is only returned if all numbers are NaN as per IEEE-754r.</p>
|
||||||
|
*
|
||||||
|
* @param a value 1
|
||||||
|
* @param b value 2
|
||||||
|
* @param c value 3
|
||||||
|
* @return the largest of the values
|
||||||
|
*/
|
||||||
|
public static float max(final float a, final float b, final float c) {
|
||||||
|
return max(max(a, b), c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the maximum of two {@code float} values.
|
||||||
|
*
|
||||||
|
* <p>NaN is only returned if all numbers are NaN as per IEEE-754r.</p>
|
||||||
|
*
|
||||||
|
* @param a value 1
|
||||||
|
* @param b value 2
|
||||||
|
* @return the largest of the values
|
||||||
|
*/
|
||||||
|
public static float max(final float a, final float b) {
|
||||||
|
if (Float.isNaN(a)) {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
if (Float.isNaN(b)) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
return Math.max(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
611
src/main/java/ch/usi/inf/sdm/sdm04/util/FluentBitSet.java
Normal file
611
src/main/java/ch/usi/inf/sdm/sdm04/util/FluentBitSet.java
Normal file
|
@ -0,0 +1,611 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package ch.usi.inf.sdm.sdm04.util;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.BitSet;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A fluent {@link BitSet} with additional operations.
|
||||||
|
* <p>
|
||||||
|
* Originally from Apache Commons VFS with more added to act as a fluent replacement for {@link BitSet}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @since 3.13.0
|
||||||
|
*/
|
||||||
|
public final class FluentBitSet implements Cloneable, Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Working BitSet.
|
||||||
|
*/
|
||||||
|
private final BitSet bitSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new bit set. All bits are initially {@code false}.
|
||||||
|
*/
|
||||||
|
public FluentBitSet() {
|
||||||
|
this(new BitSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance for the given bit set.
|
||||||
|
*
|
||||||
|
* @param set The bit set to wrap.
|
||||||
|
*/
|
||||||
|
public FluentBitSet(final BitSet set) {
|
||||||
|
this.bitSet = Objects.requireNonNull(set, "set");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a bit set whose initial size is large enough to explicitly represent bits with indices in the range {@code 0}
|
||||||
|
* through {@code nbits-1}. All bits are initially {@code false}.
|
||||||
|
*
|
||||||
|
* @param nbits the initial size of the bit set.
|
||||||
|
* @throws NegativeArraySizeException if the specified initial size is negative.
|
||||||
|
*/
|
||||||
|
public FluentBitSet(final int nbits) {
|
||||||
|
this(new BitSet(nbits));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a logical <b>AND</b> of this target bit set with the argument bit set. This bit set is modified so that each
|
||||||
|
* bit in it has the value {@code true} if and only if it both initially had the value {@code true} and the
|
||||||
|
* corresponding bit in the bit set argument also had the value {@code true}.
|
||||||
|
*
|
||||||
|
* @param set a bit set.
|
||||||
|
* @return this.
|
||||||
|
*/
|
||||||
|
public FluentBitSet and(final BitSet set) {
|
||||||
|
bitSet.and(set);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a logical <b>AND</b> of this target bit set with the argument bit set. This bit set is modified so that each
|
||||||
|
* bit in it has the value {@code true} if and only if it both initially had the value {@code true} and the
|
||||||
|
* corresponding bit in the bit set argument also had the value {@code true}.
|
||||||
|
*
|
||||||
|
* @param set a bit set.
|
||||||
|
* @return this.
|
||||||
|
*/
|
||||||
|
public FluentBitSet and(final FluentBitSet set) {
|
||||||
|
bitSet.and(set.bitSet);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears all of the bits in this {@link BitSet} whose corresponding bit is set in the specified {@link BitSet}.
|
||||||
|
*
|
||||||
|
* @param set the {@link BitSet} with which to mask this {@link BitSet}.
|
||||||
|
* @return this.
|
||||||
|
*/
|
||||||
|
public FluentBitSet andNot(final BitSet set) {
|
||||||
|
bitSet.andNot(set);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears all of the bits in this {@link BitSet} whose corresponding bit is set in the specified {@link BitSet}.
|
||||||
|
*
|
||||||
|
* @param set the {@link BitSet} with which to mask this {@link BitSet}.
|
||||||
|
* @return this.
|
||||||
|
*/
|
||||||
|
public FluentBitSet andNot(final FluentBitSet set) {
|
||||||
|
this.bitSet.andNot(set.bitSet);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the wrapped bit set.
|
||||||
|
*
|
||||||
|
* @return the wrapped bit set.
|
||||||
|
*/
|
||||||
|
public BitSet bitSet() {
|
||||||
|
return bitSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of bits set to {@code true} in this {@link BitSet}.
|
||||||
|
*
|
||||||
|
* @return the number of bits set to {@code true} in this {@link BitSet}.
|
||||||
|
*/
|
||||||
|
public int cardinality() {
|
||||||
|
return bitSet.cardinality();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets all of the bits in this BitSet to {@code false}.
|
||||||
|
*
|
||||||
|
* @return this.
|
||||||
|
*/
|
||||||
|
public FluentBitSet clear() {
|
||||||
|
bitSet.clear();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the bits specified by the indexes to {@code false}.
|
||||||
|
*
|
||||||
|
* @param bitIndexArray the index of the bit to be cleared.
|
||||||
|
* @return this.
|
||||||
|
* @throws IndexOutOfBoundsException if the specified index is negative.
|
||||||
|
*/
|
||||||
|
public FluentBitSet clear(final int... bitIndexArray) {
|
||||||
|
for (final int e : bitIndexArray) {
|
||||||
|
this.bitSet.clear(e);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the bit specified by the index to {@code false}.
|
||||||
|
*
|
||||||
|
* @param bitIndex the index of the bit to be cleared.
|
||||||
|
* @return this.
|
||||||
|
* @throws IndexOutOfBoundsException if the specified index is negative.
|
||||||
|
*/
|
||||||
|
public FluentBitSet clear(final int bitIndex) {
|
||||||
|
bitSet.clear(bitIndex);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the bits from the specified {@code fromIndex} (inclusive) to the specified {@code toIndex} (exclusive) to
|
||||||
|
* {@code false}.
|
||||||
|
*
|
||||||
|
* @param fromIndex index of the first bit to be cleared.
|
||||||
|
* @param toIndex index after the last bit to be cleared.
|
||||||
|
* @return this.
|
||||||
|
* @throws IndexOutOfBoundsException if {@code fromIndex} is negative, or {@code toIndex} is negative, or
|
||||||
|
* {@code fromIndex} is larger than {@code toIndex}.
|
||||||
|
*/
|
||||||
|
public FluentBitSet clear(final int fromIndex, final int toIndex) {
|
||||||
|
bitSet.clear(fromIndex, toIndex);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cloning this {@link BitSet} produces a new {@link BitSet} that is equal to it. The clone of the bit set is another
|
||||||
|
* bit set that has exactly the same bits set to {@code true} as this bit set.
|
||||||
|
*
|
||||||
|
* @return a clone of this bit set
|
||||||
|
* @see #size()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Object clone() {
|
||||||
|
return new FluentBitSet((BitSet) bitSet.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(final Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(obj instanceof FluentBitSet)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final FluentBitSet other = (FluentBitSet) obj;
|
||||||
|
return Objects.equals(bitSet, other.bitSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the bit at the specified index to the complement of its current value.
|
||||||
|
*
|
||||||
|
* @param bitIndex the index of the bit to flip.
|
||||||
|
* @return this.
|
||||||
|
* @throws IndexOutOfBoundsException if the specified index is negative.
|
||||||
|
*/
|
||||||
|
public FluentBitSet flip(final int bitIndex) {
|
||||||
|
bitSet.flip(bitIndex);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets each bit from the specified {@code fromIndex} (inclusive) to the specified {@code toIndex} (exclusive) to the
|
||||||
|
* complement of its current value.
|
||||||
|
*
|
||||||
|
* @param fromIndex index of the first bit to flip.
|
||||||
|
* @param toIndex index after the last bit to flip.
|
||||||
|
* @return this.
|
||||||
|
* @throws IndexOutOfBoundsException if {@code fromIndex} is negative, or {@code toIndex} is negative, or
|
||||||
|
* {@code fromIndex} is larger than {@code toIndex}.
|
||||||
|
*/
|
||||||
|
public FluentBitSet flip(final int fromIndex, final int toIndex) {
|
||||||
|
bitSet.flip(fromIndex, toIndex);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value of the bit with the specified index. The value is {@code true} if the bit with the index
|
||||||
|
* {@code bitIndex} is currently set in this {@link BitSet}; otherwise, the result is {@code false}.
|
||||||
|
*
|
||||||
|
* @param bitIndex the bit index.
|
||||||
|
* @return the value of the bit with the specified index.
|
||||||
|
* @throws IndexOutOfBoundsException if the specified index is negative.
|
||||||
|
*/
|
||||||
|
public boolean get(final int bitIndex) {
|
||||||
|
return bitSet.get(bitIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new {@link BitSet} composed of bits from this {@link BitSet} from {@code fromIndex} (inclusive) to
|
||||||
|
* {@code toIndex} (exclusive).
|
||||||
|
*
|
||||||
|
* @param fromIndex index of the first bit to include.
|
||||||
|
* @param toIndex index after the last bit to include.
|
||||||
|
* @return a new {@link BitSet} from a range of this {@link BitSet}.
|
||||||
|
* @throws IndexOutOfBoundsException if {@code fromIndex} is negative, or {@code toIndex} is negative, or
|
||||||
|
* {@code fromIndex} is larger than {@code toIndex}.
|
||||||
|
*/
|
||||||
|
public FluentBitSet get(final int fromIndex, final int toIndex) {
|
||||||
|
return new FluentBitSet(bitSet.get(fromIndex, toIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return bitSet.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the specified {@link BitSet} has any bits set to {@code true} that are also set to {@code true} in
|
||||||
|
* this {@link BitSet}.
|
||||||
|
*
|
||||||
|
* @param set {@link BitSet} to intersect with.
|
||||||
|
* @return boolean indicating whether this {@link BitSet} intersects the specified {@link BitSet}.
|
||||||
|
*/
|
||||||
|
public boolean intersects(final BitSet set) {
|
||||||
|
return bitSet.intersects(set);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the specified {@link BitSet} has any bits set to {@code true} that are also set to {@code true} in
|
||||||
|
* this {@link BitSet}.
|
||||||
|
*
|
||||||
|
* @param set {@link BitSet} to intersect with.
|
||||||
|
* @return boolean indicating whether this {@link BitSet} intersects the specified {@link BitSet}.
|
||||||
|
*/
|
||||||
|
public boolean intersects(final FluentBitSet set) {
|
||||||
|
return bitSet.intersects(set.bitSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this {@link BitSet} contains no bits that are set to {@code true}.
|
||||||
|
*
|
||||||
|
* @return boolean indicating whether this {@link BitSet} is empty.
|
||||||
|
*/
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return bitSet.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the "logical size" of this {@link BitSet}: the index of the highest set bit in the {@link BitSet} plus one.
|
||||||
|
* Returns zero if the {@link BitSet} contains no set bits.
|
||||||
|
*
|
||||||
|
* @return the logical size of this {@link BitSet}.
|
||||||
|
*/
|
||||||
|
public int length() {
|
||||||
|
return bitSet.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the index of the first bit that is set to {@code false} that occurs on or after the specified starting index.
|
||||||
|
*
|
||||||
|
* @param fromIndex the index to start checking from (inclusive).
|
||||||
|
* @return the index of the next clear bit.
|
||||||
|
* @throws IndexOutOfBoundsException if the specified index is negative.
|
||||||
|
*/
|
||||||
|
public int nextClearBit(final int fromIndex) {
|
||||||
|
return bitSet.nextClearBit(fromIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the index of the first bit that is set to {@code true} that occurs on or after the specified starting index.
|
||||||
|
* If no such bit exists then {@code -1} is returned.
|
||||||
|
* <p>
|
||||||
|
* To iterate over the {@code true} bits in a {@link BitSet}, use the following loop:
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* {@code
|
||||||
|
* for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i+1)) {
|
||||||
|
* // operate on index i here
|
||||||
|
* if (i == Integer.MAX_VALUE) {
|
||||||
|
* break; // or (i+1) would overflow
|
||||||
|
* }
|
||||||
|
* }}
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param fromIndex the index to start checking from (inclusive).
|
||||||
|
* @return the index of the next set bit, or {@code -1} if there is no such bit.
|
||||||
|
* @throws IndexOutOfBoundsException if the specified index is negative.
|
||||||
|
*/
|
||||||
|
public int nextSetBit(final int fromIndex) {
|
||||||
|
return bitSet.nextSetBit(fromIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a logical <b>OR</b> of this bit set with the bit set argument. This bit set is modified so that a bit in it
|
||||||
|
* has the value {@code true} if and only if it either already had the value {@code true} or the corresponding bit in
|
||||||
|
* the bit set argument has the value {@code true}.
|
||||||
|
*
|
||||||
|
* @param set a bit set.
|
||||||
|
* @return this.
|
||||||
|
*/
|
||||||
|
public FluentBitSet or(final BitSet set) {
|
||||||
|
bitSet.or(set);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a logical <b>OR</b> of this bit set with the bit set arguments. This bit set is modified so that a bit in it
|
||||||
|
* has the value {@code true} if and only if it either already had the value {@code true} or the corresponding bit in
|
||||||
|
* the bit set argument has the value {@code true}.
|
||||||
|
*
|
||||||
|
* @param set a bit set.
|
||||||
|
* @return this.
|
||||||
|
*/
|
||||||
|
public FluentBitSet or(final FluentBitSet... set) {
|
||||||
|
for (final FluentBitSet e : set) {
|
||||||
|
this.bitSet.or(e.bitSet);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a logical <b>OR</b> of this bit set with the bit set argument. This bit set is modified so that a bit in it
|
||||||
|
* has the value {@code true} if and only if it either already had the value {@code true} or the corresponding bit in
|
||||||
|
* the bit set argument has the value {@code true}.
|
||||||
|
*
|
||||||
|
* @param set a bit set.
|
||||||
|
* @return this.
|
||||||
|
*/
|
||||||
|
public FluentBitSet or(final FluentBitSet set) {
|
||||||
|
this.bitSet.or(set.bitSet);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the index of the nearest bit that is set to {@code false} that occurs on or before the specified starting
|
||||||
|
* index. If no such bit exists, or if {@code -1} is given as the starting index, then {@code -1} is returned.
|
||||||
|
*
|
||||||
|
* @param fromIndex the index to start checking from (inclusive).
|
||||||
|
* @return the index of the previous clear bit, or {@code -1} if there is no such bit.
|
||||||
|
* @throws IndexOutOfBoundsException if the specified index is less than {@code -1}.
|
||||||
|
*/
|
||||||
|
public int previousClearBit(final int fromIndex) {
|
||||||
|
return bitSet.previousClearBit(fromIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the index of the nearest bit that is set to {@code true} that occurs on or before the specified starting
|
||||||
|
* index. If no such bit exists, or if {@code -1} is given as the starting index, then {@code -1} is returned.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* To iterate over the {@code true} bits in a {@link BitSet}, use the following loop:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* {@code
|
||||||
|
* for (int i = bs.length(); (i = bs.previousSetBit(i-1)) >= 0; ) {
|
||||||
|
* // operate on index i here
|
||||||
|
* }}
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param fromIndex the index to start checking from (inclusive)
|
||||||
|
* @return the index of the previous set bit, or {@code -1} if there is no such bit
|
||||||
|
* @throws IndexOutOfBoundsException if the specified index is less than {@code -1}
|
||||||
|
*/
|
||||||
|
public int previousSetBit(final int fromIndex) {
|
||||||
|
return bitSet.previousSetBit(fromIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the bit at the specified indexes to {@code true}.
|
||||||
|
*
|
||||||
|
* @param bitIndexArray a bit index array.
|
||||||
|
* @return this.
|
||||||
|
* @throws IndexOutOfBoundsException if the specified index is negative.
|
||||||
|
*/
|
||||||
|
public FluentBitSet set(final int... bitIndexArray) {
|
||||||
|
for (final int e : bitIndexArray) {
|
||||||
|
bitSet.set(e);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the bit at the specified index to {@code true}.
|
||||||
|
*
|
||||||
|
* @param bitIndex a bit index
|
||||||
|
* @return this.
|
||||||
|
* @throws IndexOutOfBoundsException if the specified index is negative
|
||||||
|
*/
|
||||||
|
public FluentBitSet set(final int bitIndex) {
|
||||||
|
bitSet.set(bitIndex);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the bit at the specified index to the specified value.
|
||||||
|
*
|
||||||
|
* @param bitIndex a bit index.
|
||||||
|
* @param value a boolean value to set.
|
||||||
|
* @return this.
|
||||||
|
* @throws IndexOutOfBoundsException if the specified index is negative.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public FluentBitSet set(final int bitIndex, final boolean value) {
|
||||||
|
bitSet.set(bitIndex, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the bits from the specified {@code fromIndex} (inclusive) to the specified {@code toIndex} (exclusive) to
|
||||||
|
* {@code true}.
|
||||||
|
*
|
||||||
|
* @param fromIndex index of the first bit to be set.
|
||||||
|
* @param toIndex index after the last bit to be set.
|
||||||
|
* @return this.
|
||||||
|
* @throws IndexOutOfBoundsException if {@code fromIndex} is negative, or {@code toIndex} is negative, or
|
||||||
|
* {@code fromIndex} is larger than {@code toIndex}.
|
||||||
|
*/
|
||||||
|
public FluentBitSet set(final int fromIndex, final int toIndex) {
|
||||||
|
bitSet.set(fromIndex, toIndex);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the bits from the specified {@code fromIndex} (inclusive) to the specified {@code toIndex} (exclusive) to the
|
||||||
|
* specified value.
|
||||||
|
*
|
||||||
|
* @param fromIndex index of the first bit to be set.
|
||||||
|
* @param toIndex index after the last bit to be set.
|
||||||
|
* @param value value to set the selected bits to.
|
||||||
|
* @return this.
|
||||||
|
* @throws IndexOutOfBoundsException if {@code fromIndex} is negative, or {@code toIndex} is negative, or
|
||||||
|
* {@code fromIndex} is larger than {@code toIndex}.
|
||||||
|
*/
|
||||||
|
public FluentBitSet set(final int fromIndex, final int toIndex, final boolean value) {
|
||||||
|
bitSet.set(fromIndex, toIndex, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the bits from the specified {@code fromIndex} (inclusive) to the specified {@code toIndex} (exclusive) to
|
||||||
|
* {@code true}.
|
||||||
|
*
|
||||||
|
* @param fromIndex index of the first bit to be set
|
||||||
|
* @param toIndex index of the last bit to be set
|
||||||
|
* @return this.
|
||||||
|
* @throws IndexOutOfBoundsException if {@code fromIndex} is negative, or {@code toIndex} is negative, or
|
||||||
|
* {@code fromIndex} is larger than {@code toIndex}
|
||||||
|
*/
|
||||||
|
public FluentBitSet setInclusive(final int fromIndex, final int toIndex) {
|
||||||
|
bitSet.set(fromIndex, toIndex + 1);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of bits of space actually in use by this {@link BitSet} to represent bit values. The maximum
|
||||||
|
* element in the set is the size - 1st element.
|
||||||
|
*
|
||||||
|
* @return the number of bits currently in this bit set.
|
||||||
|
*/
|
||||||
|
public int size() {
|
||||||
|
return bitSet.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a stream of indices for which this {@link BitSet} contains a bit in the set state. The indices are returned
|
||||||
|
* in order, from lowest to highest. The size of the stream is the number of bits in the set state, equal to the value
|
||||||
|
* returned by the {@link #cardinality()} method.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The bit set must remain constant during the execution of the terminal stream operation. Otherwise, the result of the
|
||||||
|
* terminal stream operation is undefined.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return a stream of integers representing set indices.
|
||||||
|
* @since 1.8
|
||||||
|
*/
|
||||||
|
public IntStream stream() {
|
||||||
|
return bitSet.stream();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new byte array containing all the bits in this bit set.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* More precisely, if:
|
||||||
|
* </p>
|
||||||
|
* <ol>
|
||||||
|
* <li>{@code byte[] bytes = s.toByteArray();}</li>
|
||||||
|
* <li>then {@code bytes.length == (s.length()+7)/8} and</li>
|
||||||
|
* <li>{@code s.get(n) == ((bytes[n/8] & (1<<(n%8))) != 0)}</li>
|
||||||
|
* <li>for all {@code n < 8 * bytes.length}.</li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* @return a byte array containing a little-endian representation of all the bits in this bit set
|
||||||
|
*/
|
||||||
|
public byte[] toByteArray() {
|
||||||
|
return bitSet.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new byte array containing all the bits in this bit set.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* More precisely, if:
|
||||||
|
* </p>
|
||||||
|
* <ol>
|
||||||
|
* <li>{@code long[] longs = s.toLongArray();}</li>
|
||||||
|
* <li>then {@code longs.length == (s.length()+63)/64} and</li>
|
||||||
|
* <li>{@code s.get(n) == ((longs[n/64] & (1L<<(n%64))) != 0)}</li>
|
||||||
|
* <li>for all {@code n < 64 * longs.length}.</li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* @return a byte array containing a little-endian representation of all the bits in this bit set
|
||||||
|
*/
|
||||||
|
public long[] toLongArray() {
|
||||||
|
return bitSet.toLongArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return bitSet.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a logical <b>XOR</b> of this bit set with the bit set argument. This bit set is modified so that a bit in it
|
||||||
|
* has the value {@code true} if and only if one of the following statements holds:
|
||||||
|
* <ul>
|
||||||
|
* <li>The bit initially has the value {@code true}, and the corresponding bit in the argument has the value
|
||||||
|
* {@code false}.
|
||||||
|
* <li>The bit initially has the value {@code false}, and the corresponding bit in the argument has the value
|
||||||
|
* {@code true}.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param set a bit set
|
||||||
|
* @return this.
|
||||||
|
*/
|
||||||
|
public FluentBitSet xor(final BitSet set) {
|
||||||
|
bitSet.xor(set);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a logical <b>XOR</b> of this bit set with the bit set argument. This bit set is modified so that a bit in it
|
||||||
|
* has the value {@code true} if and only if one of the following statements holds:
|
||||||
|
* <ul>
|
||||||
|
* <li>The bit initially has the value {@code true}, and the corresponding bit in the argument has the value
|
||||||
|
* {@code false}.
|
||||||
|
* <li>The bit initially has the value {@code false}, and the corresponding bit in the argument has the value
|
||||||
|
* {@code true}.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param set a bit set
|
||||||
|
* @return this.
|
||||||
|
*/
|
||||||
|
public FluentBitSet xor(final FluentBitSet set) {
|
||||||
|
bitSet.xor(set.bitSet);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
382
src/test/java/ch/usi/inf/sdm/sdm04/CharRangeTest.java
Normal file
382
src/test/java/ch/usi/inf/sdm/sdm04/CharRangeTest.java
Normal file
|
@ -0,0 +1,382 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package ch.usi.inf.sdm.sdm04;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.SerializationUtils;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests {@link CharRange}.
|
||||||
|
*/
|
||||||
|
public class CharRangeTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testClass() {
|
||||||
|
// class changed to non-public in 3.0
|
||||||
|
assertFalse(Modifier.isPublic(CharRange.class.getModifiers()));
|
||||||
|
assertTrue(Modifier.isFinal(CharRange.class.getModifiers()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConstructorAccessors_is() {
|
||||||
|
final CharRange rangea = CharRange.is('a');
|
||||||
|
assertEquals('a', rangea.getStart());
|
||||||
|
assertEquals('a', rangea.getEnd());
|
||||||
|
assertFalse(rangea.isNegated());
|
||||||
|
assertEquals("a", rangea.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConstructorAccessors_isNot() {
|
||||||
|
final CharRange rangea = CharRange.isNot('a');
|
||||||
|
assertEquals('a', rangea.getStart());
|
||||||
|
assertEquals('a', rangea.getEnd());
|
||||||
|
assertTrue(rangea.isNegated());
|
||||||
|
assertEquals("^a", rangea.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConstructorAccessors_isIn_Same() {
|
||||||
|
final CharRange rangea = CharRange.isIn('a', 'a');
|
||||||
|
assertEquals('a', rangea.getStart());
|
||||||
|
assertEquals('a', rangea.getEnd());
|
||||||
|
assertFalse(rangea.isNegated());
|
||||||
|
assertEquals("a", rangea.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConstructorAccessors_isIn_Normal() {
|
||||||
|
final CharRange rangea = CharRange.isIn('a', 'e');
|
||||||
|
assertEquals('a', rangea.getStart());
|
||||||
|
assertEquals('e', rangea.getEnd());
|
||||||
|
assertFalse(rangea.isNegated());
|
||||||
|
assertEquals("a-e", rangea.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConstructorAccessors_isIn_Reversed() {
|
||||||
|
final CharRange rangea = CharRange.isIn('e', 'a');
|
||||||
|
assertEquals('a', rangea.getStart());
|
||||||
|
assertEquals('e', rangea.getEnd());
|
||||||
|
assertFalse(rangea.isNegated());
|
||||||
|
assertEquals("a-e", rangea.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConstructorAccessors_isNotIn_Same() {
|
||||||
|
final CharRange rangea = CharRange.isNotIn('a', 'a');
|
||||||
|
assertEquals('a', rangea.getStart());
|
||||||
|
assertEquals('a', rangea.getEnd());
|
||||||
|
assertTrue(rangea.isNegated());
|
||||||
|
assertEquals("^a", rangea.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConstructorAccessors_isNotIn_Normal() {
|
||||||
|
final CharRange rangea = CharRange.isNotIn('a', 'e');
|
||||||
|
assertEquals('a', rangea.getStart());
|
||||||
|
assertEquals('e', rangea.getEnd());
|
||||||
|
assertTrue(rangea.isNegated());
|
||||||
|
assertEquals("^a-e", rangea.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConstructorAccessors_isNotIn_Reversed() {
|
||||||
|
final CharRange rangea = CharRange.isNotIn('e', 'a');
|
||||||
|
assertEquals('a', rangea.getStart());
|
||||||
|
assertEquals('e', rangea.getEnd());
|
||||||
|
assertTrue(rangea.isNegated());
|
||||||
|
assertEquals("^a-e", rangea.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEquals_Object() {
|
||||||
|
final CharRange rangea = CharRange.is('a');
|
||||||
|
final CharRange rangeae = CharRange.isIn('a', 'e');
|
||||||
|
final CharRange rangenotbf = CharRange.isIn('b', 'f');
|
||||||
|
|
||||||
|
assertNotEquals(null, rangea);
|
||||||
|
|
||||||
|
assertEquals(rangea, rangea);
|
||||||
|
assertEquals(rangea, CharRange.is('a'));
|
||||||
|
assertEquals(rangeae, rangeae);
|
||||||
|
assertEquals(rangeae, CharRange.isIn('a', 'e'));
|
||||||
|
assertEquals(rangenotbf, rangenotbf);
|
||||||
|
assertEquals(rangenotbf, CharRange.isIn('b', 'f'));
|
||||||
|
|
||||||
|
assertNotEquals(rangea, rangeae);
|
||||||
|
assertNotEquals(rangea, rangenotbf);
|
||||||
|
assertNotEquals(rangeae, rangea);
|
||||||
|
assertNotEquals(rangeae, rangenotbf);
|
||||||
|
assertNotEquals(rangenotbf, rangea);
|
||||||
|
assertNotEquals(rangenotbf, rangeae);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHashCode() {
|
||||||
|
final CharRange rangea = CharRange.is('a');
|
||||||
|
final CharRange rangeae = CharRange.isIn('a', 'e');
|
||||||
|
final CharRange rangenotbf = CharRange.isIn('b', 'f');
|
||||||
|
|
||||||
|
assertEquals(rangea.hashCode(), rangea.hashCode());
|
||||||
|
assertEquals(rangea.hashCode(), CharRange.is('a').hashCode());
|
||||||
|
assertEquals(rangeae.hashCode(), rangeae.hashCode());
|
||||||
|
assertEquals(rangeae.hashCode(), CharRange.isIn('a', 'e').hashCode());
|
||||||
|
assertEquals(rangenotbf.hashCode(), rangenotbf.hashCode());
|
||||||
|
assertEquals(rangenotbf.hashCode(), CharRange.isIn('b', 'f').hashCode());
|
||||||
|
|
||||||
|
assertNotEquals(rangea.hashCode(), rangeae.hashCode());
|
||||||
|
assertNotEquals(rangea.hashCode(), rangenotbf.hashCode());
|
||||||
|
assertNotEquals(rangeae.hashCode(), rangea.hashCode());
|
||||||
|
assertNotEquals(rangeae.hashCode(), rangenotbf.hashCode());
|
||||||
|
assertNotEquals(rangenotbf.hashCode(), rangea.hashCode());
|
||||||
|
assertNotEquals(rangenotbf.hashCode(), rangeae.hashCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testContains_Char() {
|
||||||
|
CharRange range = CharRange.is('c');
|
||||||
|
assertFalse(range.contains('b'));
|
||||||
|
assertTrue(range.contains('c'));
|
||||||
|
assertFalse(range.contains('d'));
|
||||||
|
assertFalse(range.contains('e'));
|
||||||
|
|
||||||
|
range = CharRange.isIn('c', 'd');
|
||||||
|
assertFalse(range.contains('b'));
|
||||||
|
assertTrue(range.contains('c'));
|
||||||
|
assertTrue(range.contains('d'));
|
||||||
|
assertFalse(range.contains('e'));
|
||||||
|
|
||||||
|
range = CharRange.isIn('d', 'c');
|
||||||
|
assertFalse(range.contains('b'));
|
||||||
|
assertTrue(range.contains('c'));
|
||||||
|
assertTrue(range.contains('d'));
|
||||||
|
assertFalse(range.contains('e'));
|
||||||
|
|
||||||
|
range = CharRange.isNotIn('c', 'd');
|
||||||
|
assertTrue(range.contains('b'));
|
||||||
|
assertFalse(range.contains('c'));
|
||||||
|
assertFalse(range.contains('d'));
|
||||||
|
assertTrue(range.contains('e'));
|
||||||
|
assertTrue(range.contains((char) 0));
|
||||||
|
assertTrue(range.contains(Character.MAX_VALUE));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testContains_Charrange() {
|
||||||
|
final CharRange a = CharRange.is('a');
|
||||||
|
final CharRange b = CharRange.is('b');
|
||||||
|
final CharRange c = CharRange.is('c');
|
||||||
|
final CharRange c2 = CharRange.is('c');
|
||||||
|
final CharRange d = CharRange.is('d');
|
||||||
|
final CharRange e = CharRange.is('e');
|
||||||
|
final CharRange cd = CharRange.isIn('c', 'd');
|
||||||
|
final CharRange bd = CharRange.isIn('b', 'd');
|
||||||
|
final CharRange bc = CharRange.isIn('b', 'c');
|
||||||
|
final CharRange ab = CharRange.isIn('a', 'b');
|
||||||
|
final CharRange de = CharRange.isIn('d', 'e');
|
||||||
|
final CharRange ef = CharRange.isIn('e', 'f');
|
||||||
|
final CharRange ae = CharRange.isIn('a', 'e');
|
||||||
|
|
||||||
|
// normal/normal
|
||||||
|
assertFalse(c.contains(b));
|
||||||
|
assertTrue(c.contains(c));
|
||||||
|
assertTrue(c.contains(c2));
|
||||||
|
assertFalse(c.contains(d));
|
||||||
|
|
||||||
|
assertFalse(c.contains(cd));
|
||||||
|
assertFalse(c.contains(bd));
|
||||||
|
assertFalse(c.contains(bc));
|
||||||
|
assertFalse(c.contains(ab));
|
||||||
|
assertFalse(c.contains(de));
|
||||||
|
|
||||||
|
assertTrue(cd.contains(c));
|
||||||
|
assertTrue(bd.contains(c));
|
||||||
|
assertTrue(bc.contains(c));
|
||||||
|
assertFalse(ab.contains(c));
|
||||||
|
assertFalse(de.contains(c));
|
||||||
|
|
||||||
|
assertTrue(ae.contains(b));
|
||||||
|
assertTrue(ae.contains(ab));
|
||||||
|
assertTrue(ae.contains(bc));
|
||||||
|
assertTrue(ae.contains(cd));
|
||||||
|
assertTrue(ae.contains(de));
|
||||||
|
|
||||||
|
final CharRange notb = CharRange.isNot('b');
|
||||||
|
final CharRange notc = CharRange.isNot('c');
|
||||||
|
final CharRange notd = CharRange.isNot('d');
|
||||||
|
final CharRange notab = CharRange.isNotIn('a', 'b');
|
||||||
|
final CharRange notbc = CharRange.isNotIn('b', 'c');
|
||||||
|
final CharRange notbd = CharRange.isNotIn('b', 'd');
|
||||||
|
final CharRange notcd = CharRange.isNotIn('c', 'd');
|
||||||
|
final CharRange notde = CharRange.isNotIn('d', 'e');
|
||||||
|
final CharRange notae = CharRange.isNotIn('a', 'e');
|
||||||
|
final CharRange all = CharRange.isIn((char) 0, Character.MAX_VALUE);
|
||||||
|
final CharRange allbutfirst = CharRange.isIn((char) 1, Character.MAX_VALUE);
|
||||||
|
|
||||||
|
// normal/negated
|
||||||
|
assertFalse(c.contains(notc));
|
||||||
|
assertFalse(c.contains(notbd));
|
||||||
|
assertTrue(all.contains(notc));
|
||||||
|
assertTrue(all.contains(notbd));
|
||||||
|
assertFalse(allbutfirst.contains(notc));
|
||||||
|
assertFalse(allbutfirst.contains(notbd));
|
||||||
|
|
||||||
|
// negated/normal
|
||||||
|
assertTrue(notc.contains(a));
|
||||||
|
assertTrue(notc.contains(b));
|
||||||
|
assertFalse(notc.contains(c));
|
||||||
|
assertTrue(notc.contains(d));
|
||||||
|
assertTrue(notc.contains(e));
|
||||||
|
|
||||||
|
assertTrue(notc.contains(ab));
|
||||||
|
assertFalse(notc.contains(bc));
|
||||||
|
assertFalse(notc.contains(bd));
|
||||||
|
assertFalse(notc.contains(cd));
|
||||||
|
assertTrue(notc.contains(de));
|
||||||
|
assertFalse(notc.contains(ae));
|
||||||
|
assertFalse(notc.contains(all));
|
||||||
|
assertFalse(notc.contains(allbutfirst));
|
||||||
|
|
||||||
|
assertTrue(notbd.contains(a));
|
||||||
|
assertFalse(notbd.contains(b));
|
||||||
|
assertFalse(notbd.contains(c));
|
||||||
|
assertFalse(notbd.contains(d));
|
||||||
|
assertTrue(notbd.contains(e));
|
||||||
|
|
||||||
|
assertTrue(notcd.contains(ab));
|
||||||
|
assertFalse(notcd.contains(bc));
|
||||||
|
assertFalse(notcd.contains(bd));
|
||||||
|
assertFalse(notcd.contains(cd));
|
||||||
|
assertFalse(notcd.contains(de));
|
||||||
|
assertFalse(notcd.contains(ae));
|
||||||
|
assertTrue(notcd.contains(ef));
|
||||||
|
assertFalse(notcd.contains(all));
|
||||||
|
assertFalse(notcd.contains(allbutfirst));
|
||||||
|
|
||||||
|
// negated/negated
|
||||||
|
assertFalse(notc.contains(notb));
|
||||||
|
assertTrue(notc.contains(notc));
|
||||||
|
assertFalse(notc.contains(notd));
|
||||||
|
|
||||||
|
assertFalse(notc.contains(notab));
|
||||||
|
assertTrue(notc.contains(notbc));
|
||||||
|
assertTrue(notc.contains(notbd));
|
||||||
|
assertTrue(notc.contains(notcd));
|
||||||
|
assertFalse(notc.contains(notde));
|
||||||
|
|
||||||
|
assertFalse(notbd.contains(notb));
|
||||||
|
assertFalse(notbd.contains(notc));
|
||||||
|
assertFalse(notbd.contains(notd));
|
||||||
|
|
||||||
|
assertFalse(notbd.contains(notab));
|
||||||
|
assertFalse(notbd.contains(notbc));
|
||||||
|
assertTrue(notbd.contains(notbd));
|
||||||
|
assertFalse(notbd.contains(notcd));
|
||||||
|
assertFalse(notbd.contains(notde));
|
||||||
|
assertTrue(notbd.contains(notae));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testContainsNullArg() {
|
||||||
|
final CharRange range = CharRange.is('a');
|
||||||
|
final NullPointerException e = assertThrows(NullPointerException.class, () -> range.contains(null));
|
||||||
|
assertEquals("range", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIterator() {
|
||||||
|
final CharRange a = CharRange.is('a');
|
||||||
|
final CharRange ad = CharRange.isIn('a', 'd');
|
||||||
|
final CharRange nota = CharRange.isNot('a');
|
||||||
|
final CharRange emptySet = CharRange.isNotIn((char) 0, Character.MAX_VALUE);
|
||||||
|
final CharRange notFirst = CharRange.isNotIn((char) 1, Character.MAX_VALUE);
|
||||||
|
final CharRange notLast = CharRange.isNotIn((char) 0, (char) (Character.MAX_VALUE - 1));
|
||||||
|
|
||||||
|
final Iterator<Character> aIt = a.iterator();
|
||||||
|
assertNotNull(aIt);
|
||||||
|
assertTrue(aIt.hasNext());
|
||||||
|
assertEquals(Character.valueOf('a'), aIt.next());
|
||||||
|
assertFalse(aIt.hasNext());
|
||||||
|
|
||||||
|
final Iterator<Character> adIt = ad.iterator();
|
||||||
|
assertNotNull(adIt);
|
||||||
|
assertTrue(adIt.hasNext());
|
||||||
|
assertEquals(Character.valueOf('a'), adIt.next());
|
||||||
|
assertEquals(Character.valueOf('b'), adIt.next());
|
||||||
|
assertEquals(Character.valueOf('c'), adIt.next());
|
||||||
|
assertEquals(Character.valueOf('d'), adIt.next());
|
||||||
|
assertFalse(adIt.hasNext());
|
||||||
|
|
||||||
|
final Iterator<Character> notaIt = nota.iterator();
|
||||||
|
assertNotNull(notaIt);
|
||||||
|
assertTrue(notaIt.hasNext());
|
||||||
|
while (notaIt.hasNext()) {
|
||||||
|
final Character c = notaIt.next();
|
||||||
|
assertNotEquals('a', c.charValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
final Iterator<Character> emptySetIt = emptySet.iterator();
|
||||||
|
assertNotNull(emptySetIt);
|
||||||
|
assertFalse(emptySetIt.hasNext());
|
||||||
|
assertThrows(NoSuchElementException.class, emptySetIt::next);
|
||||||
|
|
||||||
|
final Iterator<Character> notFirstIt = notFirst.iterator();
|
||||||
|
assertNotNull(notFirstIt);
|
||||||
|
assertTrue(notFirstIt.hasNext());
|
||||||
|
assertEquals(Character.valueOf((char) 0), notFirstIt.next());
|
||||||
|
assertFalse(notFirstIt.hasNext());
|
||||||
|
assertThrows(NoSuchElementException.class, notFirstIt::next);
|
||||||
|
|
||||||
|
final Iterator<Character> notLastIt = notLast.iterator();
|
||||||
|
assertNotNull(notLastIt);
|
||||||
|
assertTrue(notLastIt.hasNext());
|
||||||
|
assertEquals(Character.valueOf(Character.MAX_VALUE), notLastIt.next());
|
||||||
|
assertFalse(notLastIt.hasNext());
|
||||||
|
assertThrows(NoSuchElementException.class, notLastIt::next);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSerialization() {
|
||||||
|
CharRange range = CharRange.is('a');
|
||||||
|
assertEquals(range, SerializationUtils.clone(range));
|
||||||
|
range = CharRange.isIn('a', 'e');
|
||||||
|
assertEquals(range, SerializationUtils.clone(range));
|
||||||
|
range = CharRange.isNotIn('a', 'e');
|
||||||
|
assertEquals(range, SerializationUtils.clone(range));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIteratorRemove() {
|
||||||
|
final CharRange a = CharRange.is('a');
|
||||||
|
final Iterator<Character> aIt = a.iterator();
|
||||||
|
assertThrows(UnsupportedOperationException.class, aIt::remove);
|
||||||
|
}
|
||||||
|
}
|
1123
src/test/java/ch/usi/inf/sdm/sdm04/math/FractionTest.java
Normal file
1123
src/test/java/ch/usi/inf/sdm/sdm04/math/FractionTest.java
Normal file
File diff suppressed because it is too large
Load diff
104
src/test/java/ch/usi/inf/sdm/sdm04/math/IEEE754rUtilsTest.java
Normal file
104
src/test/java/ch/usi/inf/sdm/sdm04/math/IEEE754rUtilsTest.java
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package ch.usi.inf.sdm.sdm04.math;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests {@link org.apache.commons.lang3.math.IEEE754rUtils}.
|
||||||
|
*/
|
||||||
|
public class IEEE754rUtilsTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConstructorExists() {
|
||||||
|
new IEEE754rUtils();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEnforceExceptions() {
|
||||||
|
assertThrows(
|
||||||
|
NullPointerException.class,
|
||||||
|
() -> IEEE754rUtils.min( (float[]) null),
|
||||||
|
"IllegalArgumentException expected for null input");
|
||||||
|
|
||||||
|
assertThrows(
|
||||||
|
IllegalArgumentException.class,
|
||||||
|
IEEE754rUtils::min,
|
||||||
|
"IllegalArgumentException expected for empty input");
|
||||||
|
|
||||||
|
assertThrows(
|
||||||
|
NullPointerException.class,
|
||||||
|
() -> IEEE754rUtils.max( (float[]) null),
|
||||||
|
"IllegalArgumentException expected for null input");
|
||||||
|
|
||||||
|
assertThrows(
|
||||||
|
IllegalArgumentException.class,
|
||||||
|
IEEE754rUtils::max,
|
||||||
|
"IllegalArgumentException expected for empty input");
|
||||||
|
|
||||||
|
assertThrows(
|
||||||
|
NullPointerException.class,
|
||||||
|
() -> IEEE754rUtils.min( (double[]) null),
|
||||||
|
"IllegalArgumentException expected for null input");
|
||||||
|
|
||||||
|
assertThrows(
|
||||||
|
IllegalArgumentException.class,
|
||||||
|
IEEE754rUtils::min,
|
||||||
|
"IllegalArgumentException expected for empty input");
|
||||||
|
|
||||||
|
assertThrows(
|
||||||
|
NullPointerException.class,
|
||||||
|
() -> IEEE754rUtils.max( (double[]) null),
|
||||||
|
"IllegalArgumentException expected for null input");
|
||||||
|
|
||||||
|
assertThrows(
|
||||||
|
IllegalArgumentException.class,
|
||||||
|
IEEE754rUtils::max,
|
||||||
|
"IllegalArgumentException expected for empty input");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLang381() {
|
||||||
|
assertEquals(1.2, IEEE754rUtils.min(1.2, 2.5, Double.NaN), 0.01);
|
||||||
|
assertEquals(2.5, IEEE754rUtils.max(1.2, 2.5, Double.NaN), 0.01);
|
||||||
|
assertTrue(Double.isNaN(IEEE754rUtils.max(Double.NaN, Double.NaN, Double.NaN)));
|
||||||
|
assertEquals(1.2f, IEEE754rUtils.min(1.2f, 2.5f, Float.NaN), 0.01);
|
||||||
|
assertEquals(2.5f, IEEE754rUtils.max(1.2f, 2.5f, Float.NaN), 0.01);
|
||||||
|
assertTrue(Float.isNaN(IEEE754rUtils.max(Float.NaN, Float.NaN, Float.NaN)));
|
||||||
|
|
||||||
|
final double[] a = { 1.2, Double.NaN, 3.7, 27.0, 42.0, Double.NaN };
|
||||||
|
assertEquals(42.0, IEEE754rUtils.max(a), 0.01);
|
||||||
|
assertEquals(1.2, IEEE754rUtils.min(a), 0.01);
|
||||||
|
|
||||||
|
final double[] b = { Double.NaN, 1.2, Double.NaN, 3.7, 27.0, 42.0, Double.NaN };
|
||||||
|
assertEquals(42.0, IEEE754rUtils.max(b), 0.01);
|
||||||
|
assertEquals(1.2, IEEE754rUtils.min(b), 0.01);
|
||||||
|
|
||||||
|
final float[] aF = { 1.2f, Float.NaN, 3.7f, 27.0f, 42.0f, Float.NaN };
|
||||||
|
assertEquals(1.2f, IEEE754rUtils.min(aF), 0.01);
|
||||||
|
assertEquals(42.0f, IEEE754rUtils.max(aF), 0.01);
|
||||||
|
|
||||||
|
final float[] bF = { Float.NaN, 1.2f, Float.NaN, 3.7f, 27.0f, 42.0f, Float.NaN };
|
||||||
|
assertEquals(1.2f, IEEE754rUtils.min(bF), 0.01);
|
||||||
|
assertEquals(42.0f, IEEE754rUtils.max(bF), 0.01);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
1827
src/test/java/ch/usi/inf/sdm/sdm04/util/FluentBitSetTest.java
Normal file
1827
src/test/java/ch/usi/inf/sdm/sdm04/util/FluentBitSetTest.java
Normal file
File diff suppressed because it is too large
Load diff
Reference in a new issue