Code Examples

Package lang

unit

A unit value is defined instead of the usual () token for improved readability, e.g.:

f(((), ())) // hard to parse

import tinyscalautils.lang.unit

f((unit, unit)) // easier

InterruptibleConstructor / InterruptibleEquality

Checks the interrupted status of the current thread before instance creation and/or equality and hash code:

import tinyscalautils.lang.{ InterruptibleConstructor, InterruptibleEquality }

class C extends InterruptibleConstructor, InterruptibleEquality

val c = C() // throws InterruptedException if thread is interrupted
c == c // throws InterruptedException if thread is interrupted
c.## // throws InterruptedException if thread is interrupted

Package control

times

This is the same times as in Scalactic:

import tinyscalautils.control.times

3 times:
   println("Beetlejuice!")

limitedStack / StackOverflowException

StackOverflowException can replace StackOverflowError where the latter would not adequately be caught:

import tinyscalautils.lang.StackOverflowException
import tinyscalautils.control.limitedStack

def f(x: Int): Int = 1 + f(x + 1)

val t = Try(f(0)) // dies with StackOverflowError, no usable t value

val t = Try(limitedStack(f(0)))
t.isFailure // true
t.failed.get.isInstanceOf[StackOverflowException] // true

interruptibly

Checks the interrupted status of the current thread before running some code:

import tinyscalautils.control.interruptibly

while true do interruptibly:
   x += 1

The infinite loops terminates with InterruptedException if the thread is interrupted.

Functions or arity 1 and 2 can also be made interruptible via extensions. A similar loop can be written as:

import tinyscalautils.control.InterruptiblyExtensions

def f(n: Int) = x += n
val g = f.interruptibly

while true do g(1)

Finally, higher-order functions can have their function argument be made interruptible:

col.map.interruptibly(f)

behaves like:

col.map(f.interruptibly)

This is only implemented for functions of arity 1 and 2, which covers the most common cases:

col.forall.interruptibly(f)
col.foreach.interruptibly(f)
col.fold(start).interruptibly(f)
...

Note that:

for x <- col do interruptibly(f(x))

does not check for interrupts if col is empty, while:

col.foreach.interruptibly(f)

does.

before

An SML-like operator that evaluates two arguments and returns the value of the first one:

import tinyscalautils.control.before

var c = 0
c before (c += 1) // returns 0, c is now 1

Package collection

in

An infix operator that swaps the arguments of contains:

import tinyscalautils.collection.in

value in (1 to 10) // same as (1 to 10) contains value

circular

Produces an infinite collection of elements by continuously repeating a finite collection:

import tinyscalautils.collection.circular

val i: Iterator[Int] = List(1, 2, 3).circular
// i is an infinite iterator: 1,2,3,1,2,3,1,2,3,...

i.take(10).toList // the list 1,2,3,1,2,3,1,2,3,1

shuffle

A convenient way yo invoke Random.shuffle in a pipeline:

import tinyscalautils.collection.shuffle

given Random = ...

val s = Seq
   .fill(...)(...)
   .map(...)
   .shuffle
   .zipWithIndex

instead of:

val s = rand.shuffle( 
   Seq.fill(...)(...)
      .map(...)
   ).zipWithIndex   

randomly

Produces an infinite collection of elements randomly selected from a finite collection:

import tinyscalautils.collection.randomly

given Random = ...

val i: Iterator[Int] = List(1, 2, 3).randomly
// i is an infinite iterator of 1s, 2s and 3s, in a random order

pickOne/pickOneOption

Produces a randomly selected element from a finite, non-empty collection:

import tinyscalautils.collection.pickOne

given Random = ...

val n: Int = List(1, 2, 3).pickOne
// n is one of 1, 2 or 3, randomly chosen

Exists also as pickOneOption to handle empty collections. These methods should not be used in a loop over the same collection; use randomly instead.

JavaList

Like List.of but with mutable lists:

import tinyscalautils.collection.JavaList

val list = JavaList.of("X", "Y") // a mutable list

Lists are ArrayList by default, but LinkedList can be used as well:

import tinyscalautils.collection.JavaList
import tinyscalautils.collection.LinkedList.factory

val list = JavaList.of("X", "Y") // a mutable linked list

JavaSet

Like Set.of but with mutable sets:

import tinyscalautils.collection.JavaSet

val set = JavaSet.of("X", "Y") // a mutable set

Sets are HashSet by default, but TreeSet can be used as well:

import tinyscalautils.collection.JavaSet
import tinyscalautils.collection.TreeSet.factory

val set = JavaSet.of("X", "Y") // a mutable tree set

sortedInReverse

Like sorted, but in reverse:

import tinyscalautils.collection.sortedInReverse

val seq = ...       // a sequence
seq.sortedInReverse // same sequence as seq.sorted.reverse, but more efficient

last/lastOption

Adds last/lastOption to IterableOnce:

import tinyscalautils.collection.{ last, lastOption }

Iterator(1, 2, 3).last // 3
Iterator.empty.lastOption // None

updatedWith

Replaces a value in a sequence. The new value is computed from the old value, as an option. If the option if empty, the old value is removed:

import tinyscalautils.collection.{ deleted, updatedWith }

val list = List(A, B, C)
list.updatedWith(1)(c => Some(c.toInt))               // List(A, 66, C)
list.updatedWith(1)(c => Option.when(c > Z)(c.toInt)) // List(A, C)

Removal can also be achieved using deleted:

list.deleted(1) // List(A, C)

nonEmpty

Brings nonEmpty to Java collections:

import tinyscalautils.collection.nonEmpty

val col = java.util.HashSet[Int]()
col.nonEmpty // false

allDistinct

Checks that a collection contains no duplicates:

import tinyscalautils.collection.allDistinct
Seq(1, 2, 3, 4).allDistinct // true
Seq(1, 2, 3, 2).allDistinct // false
Seq.empty.allDistinct       // true

Package assertions

require / requireState

Simple precondition checking:

import tinyscalautils.assertions.require

// Scala style:
require(cond, s"bad argument because ${someMethod()}")

// Java style:
require(cond, "bad argument because %s", someMethod())

In either case, someMethod() is only evaluated if the condition is false.

When a requirement fails, require throws IllegalArgumentException while requireState throws IllegalStateException.

checkNonNull

A utility to help reject null with IllegalArgumentException instead of NullPointerException:

require(str.nonEmpty, "string cannot be empty") // throws NPE if str is null, Java style

import tinyscalautils.assertions.checkNonNull

require(checkNonNull(str).nonEmpty, "string cannot be empty") // throws IllegalArgumentException on null or empty string

implies

Logical implication:

val s: IndexedSeq[Int] = ...
require(!s.indices.contains(i) || s(i) > 0)

import tinyscalautils.assertions.implies

require(s.indices.contains(i) implies s(i) > 0)

The evaluation is short-circuited: RHS is evaluated only if LHS is true.

Package text

StringLetters / CharLetters

All cap letters as strings or characters:

import StringLetters.*

val list = List(A, B, C)
assert(list.tail.head == B)

short/pad

Shortens or pads strings:

import tinyscalautils.text.{ short, pad }

val str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
str.short // "ABCDEFGHIJKLMNOPQRSTUVWXYZabc..."
str.short(10) // "ABCDEFG..."
"X".pad(5) // "    X"
"X".pad(5, padding = '.') // "....X"

Note that standard method padTo pads strings on the right.

printout

Captures the output of print statements into a string:

// the string "hello"
val str = printout:
   Console.out.print("hello")
   Console.err.println(" world")

// the string "hello world\n"
val str = printout(includeErr = true):
   Console.out.print("hello")
   Console.err.println(" world")

By default, only Console.out (and Predef.print and tinyscalautils.text.print, which are based on it) is captured, but System.out and System.err are not captured. This can be changed with an option:

// the string "hello world\n"
val str = printout(includeSystem = true):
   Console.out.print("hello")
   System.out.println(" world")

Note that System.out and System.err are global variables, shared among threads, while Console.out can be different for different threads. To capture the output of newly created threads, these threads should be created within the printout function.

plural

Plural forms:

import tinyscalautils.text.plural

plural(1, "cat")           // "cat"
plural(2, "DOG")           // "DOGS"
plural(2.3, "platypus")    // "platypuses"
plural(4, "mouse", "mice") // "mice"

timeString

Human-friendly representation of a duration expressed in seconds:

import tinyscalautils.text.timeString

timeString(180.02) // "3 minutes, 20 milliseconds"
timeString(3609.732) // "1 hour, 10 seconds"
timeString(3609.732, unitsCount = 3) // "1 hour, 9 seconds, 732 milliseconds"
timeString(3609.732, unitsCount = 1) // "1 hour"

Package timing

sleep

Suspends the running thread for a specified number of seconds:

import tinyscalautils.timing.{ getTime, sleep }

sleep(3.0) // sleeps 3 seconds, starting from now

val start = getTime()
... 
sleep(3.0, start) // sleeps 3 seconds, starting from start time

This last example won't sleep at all if more than 3 seconds have already elapsed since time start.

This method sleep does not undershoot even if Thread.sleep does, and does not throw InterruptedException (but leaves interrupted threads interrupted).

Non-positive values incur no delay.

delay

Delays returning a value:

import tinyscalautils.timing.delay

val result = delay(3.0):
   // code

This produces the same value as code, but takes 3 seconds (assuming the evaluation of code took less than 3 seconds). Like sleep, also exists in a start variant: delay(3.0, start)(code).

Non-positive values incur no delay.

timeOf

The time it took to evaluate some code, in seconds:

import tinyscalautils.timing.timeOf

val time = timeOf:
   // code

The value produced by the code, if any, is ignored.

timeIt

The value produced by some code, and the time it took to compute it, as a pair:

import tinyscalautils.timing.timeIt

val (value, time) = timeIt:
   // code

Timers

Simple timers. Can be used explicitly:

import tinyscalautils.threads.Executors

val timer = Executors.newTimer(size = 2)

timer.run(2.5):
   // code
   
val future = timer.schedule(1.5):
   // code

or implicitly:

import tinyscalautils.threads.{ DelayedFuture, Executors, RunAfter }

given Timer = Executors.newTimer(2)

RunAfter(2.5):
   // code

val future = DelayedFuture(1.5):
   // code

Note that timers need to be explicitly shut down for their threads to terminate. Timers implement the AutoCloseable interface.

zipWithDuration

Asynchronously adds duration to a future:

import tinyscalautils.timing.zipWithDuration

val future: Future[String] = ...
val f: Future[(String, Double)] = future.zipWithDuration

The second half of the pair is the duration (in seconds) between the invocation of zipWithFuture and the completion of future.

slow

A mechanism to slow down sources of values:

import tinyscalautils.timing.SlowIterator

def i = Iterator.range(0, 1000)

val sum = i.sum // 499500
val sum = i.slow(10.0).sum // 499500, but in about 10 seconds

// first 10 elements delayed by about 1 second each, then fast:
i.slow(10.0, delayedElements = 10)

// first 100 elements delayed by about 0.1 second each, then fast
i.slow(10.0, delayedElements = 100)

// all elements delayed by about 0.001 second each, then 9-second delay to finish
i.slow(10.0, delayedElements = 10_000)

Also available on Source and LazyList.

runFor / callFor

Runs iterative code with a time bound:

import tinyscalautils.threads.Timer
import tinyscalautils.timing.{ runFor, callFor }

given Timer = ...

val start: T = ...
def step(st: T): Option[T] = ...

// runs step until None, at most 1 minute; returns true if no timeout
runFor(60.0)(start)(step) 

// same but also returns a Seq[T] of step results
callFor(60.0)(start)(step) 

Also a simpler form with no carried state:

def step(): Boolean = ...
runFor(60.0)(step)

and richer forms that separate state from produced values:

val start: T = ...
def step(st: T): Option[(A, T)] = ...

runFor(60.0)(start)(step) // a Boolean and a Seq[A]
val start: T = ...
def step(st: T): (A, Option[T]) = ...

runFor(60.0)(start)(step) // a Boolean and a Seq[A]

This last variant produces a last value when it terminates (i.e., (last, None)).

Package threads

orTimeout

Adds a timeout feature to future, similar to Java's:

import tinyscalautils.threads.{ orTimeout, completeOnTimeout } 

val f: Future[Int] = ...

// completes with value of f, or TimeoutException after 3 seconds
val f1: Future[Int] = f.orTimeout(3.0)

// same, but runs cancelCode after the timeout
val f2: Future[Int] = f.orTimeout(3.0, cancelCode = ...)

// completes with value of f, or of altCode if f times out after 3 seconds
// if f completes during the execution of altCode, the value of f is used
val f3: Future[Int] = f.completeOnTimeout(3.0)(altCode)

// completes with value of f, or of altCode if f times out after 3 seconds
// if f completes during the execution of altCode, the value of altCode is used
val f4: Future[Int] = f.completeOnTimeout(3.0, strict = true)(altCode)

Executors

A facility to more easily create thread pools with customized thread factories and/or rejected execution handlers. All pools have type ExecutionContextExecutorService for easier use of both Java and Scala features.

import tinyscalautils.threads.Executors

// same as java.util.concurrent.Executors.newFixedThreadPool
val exec = Executors.newThreadPool(4)

// sets a rejection policy
val exec = Executors.withRejectionPolicy(rejectionPolicy).newThreadPool(4)

// sets a "discard" rejection policy
val exec = Executors.silent.newThreadPool(4)

// sets a thread facyory
val exec = Executors.withFactory(threadFactory).newThreadPool(2)

// sets a rejection policy and a thread factory
val exec = Executors
   .withRejectionPolicy(rejectionPolicy)
   .withFactory(threadFactory)
   .newThreadPool(4)

// sets a thread factory and a rejection policy (equivalent as above)
val exec = Executors
   .withFactory(threadFactory)
   .withRejectionPolicy(rejectionPolicy)
   .newThreadPool(4)

// same as java.util.concurrent.Executors.newCachedThreadPool
val exec = Executors.newUnlimitedThreadPool(60.0)

// same as above, but terminates idle threads after 1 second instead of 1 minute
val exec = Executors.newUnlimitedThreadPool()

// sets a rejection policy and a thread factory, as with newThreadPool
val exec = Executors
   .withRejectionPolicy(rejectionPolicy)
   .withFactory(threadFactory)
   .newUnlimitedThreadPool()

A global implicit is defined as an unlimited pool with a 1-second keep-alive time (and non-daemon threads):

given ExecutionContext = tinyscalautils.threads.Executors.global

val f = Future { ... } // runs on global thread pool

run / Run/RunAfter

Runss code on an executor:

import tinyscalautils.threads.{ run, Run, RunAfter }

given exec: Executor = ...

exec.run:
   // code

Run:
   // code
   
RunAfter(2.5):
   // code   

All functions return Unit (no future).

KeepThreadsFactory

A thread factory that keeps a reference on all the threads it creates:

import tinyscalautils.threads.{ KeepThreadsFactory, Executors }

val tf = KeepThreadsFactory()
val exec = Executors.withFactory(threadFactory).newUnlimitedThreadPool()

// use the exec pool...

val threads = tf.allThreads // all created threads so far, in order of creation

The collection returned by allThreads is live: threads newly created by the factory will be added to it. The factory can be reset: threads created before the reset are discarded.

MarkedThreadsFactory

A cheaper alternative to KeepThreadsFactory: All the threads produced by the factory are distinguished by having type MarkedThread:

import tinyscalautils.threads.{ MarkedThreadsFactory, Executors }

given exec: ExecutionContextExecutorService =
   Executors.withFactory(MarkedThreadsFactory).newThreadPool(4)

val future = Future(Thread.currentThread())
val runner = Await.result(future, Duration.Inf)
runner.isInstanceOf[MarkedThread] // true

For convenience, an extension can be used to check for MarkedThread type:

import tinyscalautils.threads.isMarkedThread

runner.isMarkedThread // true

awaitTermination

A variant of the standard awaitTermination method in which the timeout is optional and specified in seconds:

import tinyscalautils.threads.awaitTermination

val exec: ExecutorService = ...

if exec.awaitTermination(5.0) ... // waits at most 5 seconds
if exec.awaitTermination() ...    // waits indefinitely

shutdownAndWait

Combines shutdown, shutdownNow and awaitTermination in one method:

import tinyscalautils.threads.shutdownAndWait

val exec: ExecutorService = ...

if exec.shutdownAndWait(5.0)
   then // shutdown was invoked, and pool terminated within 5 seconds
   else // shutdown was invoked, but pool did not terminate within 5 seconds

if exec.shutdownAndWait(5.0, force = true)
   then // shutdown was invoked, and pool terminated within 5 seconds
   else // shutdown was invoked, then after 5 seconds, shutdownNow was invoked
   
exec.shutdownAndWait() // invokes shutdown and waits indefinitely; returns true

await

Adds a variant of await on CountDownLatch that specifies its timeout in seconds:

import tinyscalautils.threads.await

val latch: CountDownLatch = ...

latch.await(5.0)

countDownAndWait

Combines countDown and await:

import tinyscalautils.threads.countDownAndWait

val latch: CountDownLatch = ...

latch.countDownAndWait()
latch.countDownAndWait(3.0)

Waits forever (interruptibly) if no timeout is specified.

acquire

Adds a variant of acquire on Semaphore that specifies its timeout in seconds:

import tinyscalautils.threads.acquire

val sem: Semaphore = ...

sem.acquire(1, seconds = 3.0)

offer / poll

Adds variant of offer and poll on blocking queues that specifies their timeout in seconds:

import tinyscalautils.threads.{ offer, poll }

val q = ArrayBlockingQueue[String](1)
q.offer("X", seconds = 1.0) // true, immediately
q.offer("X", seconds = 1.0) // false, after 1 second
q.poll(seconds = 1.0)       // "X", immediately
q.poll(seconds = 1.0)       // null, after 1 second

joined

Combines join and isAlive:

import tinyscalautils.threads.joined

if thread.joined(3.0)
   then ... // thread is terminated (or never started)
   else ... // 3-second timeout; thread may still be running                

isSpinning

Checks if a thread is spinning:

import tinyscalautils.threads.isSpinning

if thread.isSpinning(seconds = 5.0, threshold = 0.1) then ...

The method detects if a thread has spent more than a specified threshold of time (here, 0.1 or 10%) using CPU during a specified time span (here, 5 seconds). The default is to check for 1 second using a 1% threshold.

This functionality may of may not be supported by a given platform (see java.lang.management.ThreadMXBean).

newThread

Kotlin-like functions for easier thread creation:

import tinyscalautils.threads.newThread 

val thread = newThread(name = "Joe", start = false, daemon = true, waitForChildren = false):
   // code

All arguments have default values and are optional. Be aware that waitForChildren only works if children are created within the same thread group (which is not always the case for the default thread factory of java.util.concurrent.Executors).

Execution contexts

Easy setup of execution contexts, mostly for testing:

import tinyscalautils.threads.withThreads

val result = withThreads(4):
   Future(42)

This creates a 4-thread pool, runs the future on it, waits for the future to finish, shuts down the thread pool, and sets result to 42. Additionally, if awaitTermination is set to true, the constructs also waits for the thread pool to terminate before setting the result. All waiting is done without a timeout. Exists also as a withThreads() variant for an unbounded thread pool.

The construct can also be used with code that does not produce a future. It returns the value produces by the code.

Instead of creating (and shutting down) a new thread pool, the construct can also reuse an existing thread pool:

val exec = Executors.newUnlimitedThreadPool()
val result = withThreads(exec):
   Future(42)

This runs the future on the thread pool and waits for the future to finish before setting result to 42. The thread pool is left as-is. Alternatively, setting shutdown to true shuts down the thread pool after the future has completed (but does not not wait for the thread pool to terminate).

Note that if shutdown is known to be false at compile time, the thread pool doesn't need a shutdown method. In particular, it can be of type Executor or ExecutionContext:

withThreads(ExecutionContext.global, shutdown = false):
   Future:
      ...

This runs the future on the global execution context, and waits for its termination.

runAsync

Run code synchronously in another thread:

import tinyscalautils.threads.runAsync

val result = runAsync(code)

The thread is specified as an implicit Executor or ExecutionContext.

The purpose of this function is to run non-interruptible code interruptibly.

Package io

listPaths

The contents of a directory, as a list:

import tinyscalautils.io.listPaths

for path: Path <- listPaths(dir) do ...

If a list type is not suitable, use readPaths instead:

import tinyscalautils.io.readPaths

val paths: Set[Path] = readPaths(Set)(dir)

DO NOT use a stream for the collection. Since the directory is closed before this function finishes, lazily evaluated collections won't work.

read/readAll

The contents of a UTF8 text file, as a collection of line elements:

import tinyscalautils.io.{ readAll, FileIsInput }

def parse(line: String): Option[Int] = ...

readAll(IndexedSeq)(file, parse)     // an IndexedSeq[Int]
readAll(IndexedSeq)(file)            // all non-blank lines
readAll(IndexedSeq)(file, noParsing) // all lines

If no factory is specified, it defaults to List, e.g.:

readAll(file, parse)     // a List[Int]
readAll(file)            // a List[String]
readAll(file, noParsing) // a List[String]

read does no parsing and returns the entire file contents as a single string.

The argument file must belong to the Input type class, which is predefined to contain InputStream, URL, URI, Path, File and String (as a filename or a URL).

DO NOT use a stream for the collection. Since the source is closed before this function finishes, lazily evaluated collections won't work.

readingAll

This is a variant of readAll that returns values as a closeable iterator:

import tinyscalautils.io.{ readingAll, FileIsInput }

def parse(line: String): Option[Int] = ...

Using.resource(readingAll(file, parse)): i =>
   // i has type Iterator[Int]
// file input closed here

readingAll does not support a silent mode.

write/writeAll

Write data to a UTF8 text file:

   import tinyscalautils.io.{ write, writeAll, FileIsOutput }
   
   val list = List(1, 2, 3)

   write(file)(list)                                      // file contains "List(1, 2, 3)"
   write(file, newline = true)(list)                      // file contains "List(1, 2, 3)\n"
   writeAll(file)(list)                                   // file contains "1\n2\n3\n"
   writeAll(sep = ",")(file)(list)                        // file contains "1,2,3"
   writeAll(pre = "[", sep = ",", post = "]")(file)(list) // file contains "[1,2,3]"
   writeAll()(file)(list)                                 // file contains "123"

Note that the simplified writeAll variant includes a final newline after the last value.

The argument file must belong to the Output type class, which is predefined to contain OutputStream, Path, File and String (as a filename).

findResource/findResourceAsStream

import tinyscalautils.io.findResource

val url = this.findResource(name)

This is equivalent to getClass.getResource(name), except that it throws an exception for missing resources (instead of returning null). In particular, a leading slash in the resource name makes it an absolute path, instead of being associated with the package name by default.

val url = this.findResource(fallback)(name)

This variant uses a fallback URI instead of failing with MissingResourceException.

The findResourceAsStream variants work in the same way but looks for a GZIP compressed variant of a resource before giving up.

Package util

FastRandom

Faster random number generators that don't rely on the java.util.Random thread-safe implementation. Individual instances are not thread-safe, but the singleton FastRandom can be shared among threads without contention:

import tinyscalautils.util.FastRandom
impot scala.util.Random

val rand: Random = FastRandom(42L) // fast, but not thread-safe

val rand: Random = FastRandom // fast, thread-safe, but cannot be seeded

The implementation relies on ThreadLocalRandom and SplittableRandom.

nextInt

A Kotlin-inspired alternative to pickOne to select random numbers from a range:

import tinyscalautils.util.nextInt
impot scala.util.Random

val rand: Random = ...

rand.nextInt(1 to 10)
rand.nextInt(0 until 100)

This looks nicer than (1 to 10).pickone(using rand) and (0 until 100).pickone(using rand).

interruptible

Makes all the methods of Random interruptible, i.e., rand.interruptible.nextInt() is the same as interruptibly(rand.nextInt()).

Note that rand.interruptible.self does not check for interrupts.

log2

Base 2 integer logarithm:

import tinyscalautils.util.log2

log2(1) // 0
log2(7) // 2
log2(8) // 3
log2(1L << 62) // 62

average

Calculates an average by ignoring a fixed number of low/high values:

val nums: Seq[BigDecimal] = Seq(12, 1, 7, 11, 14, 9)

average(nums)    // (1 + 7 + 9 + 11 + 12 + 14) / 6
average(nums, 1) // (7 + 9 + 11 + 12) / 4
average(nums, 2) // (9 + 11) / 2
average(nums, 3) // 0

The function works on any Fractional type, including Double.

dot/star

An identity function that prints a single dot (or star):

import tinyscalautils.util.dot

val x = dot(y)  // x eq y and a dot was printed
val x = star(y) // x eq y and a star was printed

Importing tinyscalautils.text.silentMode stops the dot/star from being printed. Printing modes other than silent and standard cannot be used.

isZero

A zero test on numeric types:

import tinyscalautils.util.isZero

val n: Long = 0
n.isZero && !(n + 1).isZero // true

val n: BigDecimal = 0
n.isZero && !(n + 1).isZero // true

The intent is to be more efficient (maybe) than n == 0 on some types.

isEven/isOdd

Check if numbers are even/odd:

import tinyscalautils.util.{ isEven, isOdd }

val n = 2
n.isEven      // true
(n + 1).isOdd // true

This is implemented for both Int and Long.

pow

Integer power of a number:

val n: Int = ...
val m: BigDecimal = ...
val p: Double = ...

n.pow(7)
m.pow(7)
p.pow(7)

This is implemented for the Numeric classtype, with optimizations for common types.