tinymath 0.1.0

Experimental routines for performing arithmetic on small data types
Documentation
// Copyright 2023 Google LLC
//
// Licensed 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
//
//     https://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.

use tinymath::i8_mul_div_128;
use tinymath::i8_mul_div_128_reference;
use tinymath::saturating_i16_mul_div_1024;
use tinymath::saturating_i16_mul_div_1024_reference;

#[test]
fn test_i8_mul_div_128() {
    assert_eq!(25, i8_mul_div_128(50, 64));
    assert_eq!(25, i8_mul_div_128(64, 50));
    assert_eq!(-25, i8_mul_div_128(-50, 64));
    assert_eq!(-25, i8_mul_div_128(64, -50));
    assert_eq!(-25, i8_mul_div_128(50, -64));
    assert_eq!(-25, i8_mul_div_128(-64, 50));
    assert_eq!(25, i8_mul_div_128(-50, -64));
    assert_eq!(25, i8_mul_div_128(-64, -50));

    let values = [0i8, 1, 2, 3, 4, 5, 10, 16, 20, 32, 40, 63, 64, 65, 90, 127];
    for a in values.iter() {
        for b in values.iter() {
            for (p, q) in [(1, 1), (1, -1), (-1, 1), (-1, -1)] {
                let a = a * p;
                let b = b * q;
                let expected = i8_mul_div_128_reference(a, b);
                let actual = i8_mul_div_128(a, b);
                assert_eq!(expected, actual, "{a}*{b}");
            }
        }
    }
}

fn check_i16_mul_div_1024(a: u16, b: u16) {
    for (p, q) in [(1, 1), (1, -1), (-1, 1), (-1, -1)] {
        let a = a as i16 * p;
        let b = b as i16 * q;
        let expected = saturating_i16_mul_div_1024_reference(a, b);
        let actual = saturating_i16_mul_div_1024(a, b);
        assert_eq!(expected, actual, "{a}*{b}");
    }
}

#[test]
fn test_i16_mul_div_1024() {
    assert_eq!(200, saturating_i16_mul_div_1024(400, 512));
    assert_eq!(200, saturating_i16_mul_div_1024(512, 400));
    assert_eq!(-200, saturating_i16_mul_div_1024(-400, 512));
    assert_eq!(-200, saturating_i16_mul_div_1024(512, -400));
    assert_eq!(-200, saturating_i16_mul_div_1024(400, -512));
    assert_eq!(-200, saturating_i16_mul_div_1024(-512, 400));
    assert_eq!(200, saturating_i16_mul_div_1024(-400, -512));
    assert_eq!(200, saturating_i16_mul_div_1024(-512, -400));

    assert_eq!(-3200, saturating_i16_mul_div_1024(i16::MIN, 100));
    assert_eq!(-3200, saturating_i16_mul_div_1024(100, i16::MIN));

    let values = [
        0, 1, 2, 3, 10, 16, 20, 63, 64, 65, 90, 128, 511, 512, 1023, 1024, 4000,
    ];
    for a in values.iter() {
        for b in values.iter() {
            check_i16_mul_div_1024(*a, *b);
        }
    }
}

#[test]
fn test_saturating_i16_mul_div_1024() {
    // Note: The non-saturating behavior is tested above

    // These values are barely in bounds:
    check_i16_mul_div_1024(4200, 7500);
    check_i16_mul_div_1024(7500, 4200);
    check_i16_mul_div_1024(1000, 32000);
    check_i16_mul_div_1024(32000, 1000);
    check_i16_mul_div_1024(i16::MAX as u16, 1000);
    check_i16_mul_div_1024(1000, i16::MAX as u16);

    // These values exceed bounds:
    check_i16_mul_div_1024(8000, 8000);
    check_i16_mul_div_1024(2000, 32000);
    check_i16_mul_div_1024(32000, 2000);
    check_i16_mul_div_1024(32000, 32000);
    check_i16_mul_div_1024(24576, 24576);
    check_i16_mul_div_1024(i16::MAX as u16, i16::MAX as u16);

    // Test saturating behavior with i16::MIN:
    assert_eq!(i16::MIN + 1, saturating_i16_mul_div_1024(1024, i16::MIN));
    assert_eq!(i16::MIN + 1, saturating_i16_mul_div_1024(i16::MIN, 1024));
    assert_eq!(i16::MIN + 1, saturating_i16_mul_div_1024(2000, i16::MIN));
    assert_eq!(i16::MIN + 1, saturating_i16_mul_div_1024(i16::MIN, 2000));
    assert_eq!(i16::MAX, saturating_i16_mul_div_1024(i16::MIN, i16::MIN));
}