hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

arithm.ha (8114B)


      1 // SPDX-License-Identifier: MPL-2.0
      2 // (c) Hare authors <https://harelang.org>
      3 
      4 use math;
      5 
      6 // Adds a [[duration]] to an [[instant]], returning an instant further in the
      7 // future (given a positive duration), or further in the past (given a negative
      8 // duration).
      9 export fn add(i: instant, x: duration) instant = {
     10 	if (x == 0) {
     11 		return i;
     12 	} else if (x > 0) {
     13 		return instant {
     14 			sec = i.sec + (i.nsec + x) / SECOND,
     15 			nsec = (i.nsec + x) % SECOND,
     16 		};
     17 	} else {
     18 		return instant {
     19 			sec = i.sec + (i.nsec + x - SECOND + NANOSECOND) / SECOND,
     20 			nsec = (i.nsec + (x % SECOND) + SECOND) % SECOND,
     21 		};
     22 	};
     23 };
     24 
     25 // Returns the [[duration]] from [[instant]] "a" to [[instant]] "b".
     26 export fn diff(a: instant, b: instant) duration = {
     27 	return ((b.sec - a.sec) * SECOND) + (b.nsec - a.nsec);
     28 };
     29 
     30 // Returns -1 if a precedes b, 0 if a and b are simultaneous, or +1 if b
     31 // precedes a.
     32 export fn compare(a: instant, b: instant) i8 = {
     33 	return if (a.sec < b.sec) -1
     34 	else if (a.sec > b.sec) 1
     35 	else if (a.nsec < b.nsec) -1
     36 	else if (a.nsec > b.nsec) 1
     37 	else 0;
     38 };
     39 
     40 // Scales the given [[instant]]'s scalar value by a factor 'f'. Make sure to
     41 // know what epoch you're dealing with.
     42 export fn mult(i: instant, f: f64) instant = {
     43 	// use positive numbers for convenience
     44 	const positive = if (i.sec < 0 ^^ f < 0.0) false else true;
     45 	const f = if (f < 0.0) -f else f;
     46 	const asec: i64 = if (i.sec < 0) -i.sec - 1 else i.sec;
     47 	const ansec: i64 = if (i.sec < 0) SECOND - i.nsec else i.nsec;
     48 
     49 	// initial multiply
     50 	const fsec = (asec: f64 * f);
     51 	const bsec = fsec: i64;
     52 	const fnsec = (ansec: f64 * f);
     53 	const bnsec = fnsec: i64;
     54 
     55 	// get seconds overflow (nsec remainder)
     56 	const secrem = math::modf64(fsec, 1.0);
     57 	const addnsec = (secrem * SECOND: f64): i64;
     58 
     59 	// add overflows
     60 	const b = instant {
     61 		sec = bsec,
     62 		nsec = ansec,
     63 	};
     64 	const b = add(b, bnsec - ansec); // add nsec overflow
     65 	const b = add(b, addnsec);       // add sec overflow
     66 
     67 	// switch back to original sign
     68 	const b = if (positive) {
     69 		yield b;
     70 	} else {
     71 		yield instant {
     72 			sec = -b.sec - 1,
     73 			nsec = SECOND - b.nsec,
     74 		};
     75 	};
     76 
     77 	return b;
     78 };
     79 
     80 @test fn add() void = {
     81 	const cases = [
     82 	//	instant a        duration x    instant b
     83 		( 0,         0,  -2000000001,  -3, 999999999),
     84 		( 0,         0,  -2000000000,  -2,         0),
     85 		( 0,         0,  -1999999999,  -2,         1),
     86 		( 0,         0,  -1000000001,  -2, 999999999),
     87 		( 0,         0,  -1000000000,  -1,         0),
     88 		( 0,         0,   -999999999,  -1,         1),
     89 		( 0,         0,           -1,  -1, 999999999),
     90 		( 0,         0,            0,   0,         0),
     91 		( 0,         0,            1,   0,         1),
     92 		( 0,         0,    999999999,   0, 999999999),
     93 		( 0,         0,   1000000000,   1,         0),
     94 		( 0,         0,   1000000001,   1,         1),
     95 		( 0,         0,   1999999999,   1, 999999999),
     96 		( 0,         0,   2000000000,   2,         0),
     97 		( 0,         0,   2000000001,   2,         1),
     98 
     99 		( 0,         1,  -2000000001,  -2,         0),
    100 		( 0,         1,  -2000000000,  -2,         1),
    101 		( 0,         1,  -1999999999,  -2,         2),
    102 		( 0,         1,  -1000000001,  -1,         0),
    103 		( 0,         1,  -1000000000,  -1,         1),
    104 		( 0,         1,   -999999999,  -1,         2),
    105 		( 0,         1,           -1,   0,         0),
    106 		( 0,         1,            0,   0,         1),
    107 		( 0,         1,            1,   0,         2),
    108 		( 0,         1,    999999999,   1,         0),
    109 		( 0,         1,   1000000000,   1,         1),
    110 		( 0,         1,   1000000001,   1,         2),
    111 		( 0,         1,   1999999999,   2,         0),
    112 		( 0,         1,   2000000000,   2,         1),
    113 		( 0,         1,   2000000001,   2,         2),
    114 
    115 		(-1, 999999999,  -2000000001,  -3, 999999998),
    116 		(-1, 999999999,  -2000000000,  -3, 999999999),
    117 		(-1, 999999999,  -1999999999,  -2,         0),
    118 		(-1, 999999999,  -1000000001,  -2, 999999998),
    119 		(-1, 999999999,  -1000000000,  -2, 999999999),
    120 		(-1, 999999999,  - 999999999,  -1,         0),
    121 		(-1, 999999999,           -1,  -1, 999999998),
    122 		(-1, 999999999,            0,  -1, 999999999),
    123 		(-1, 999999999,            1,   0,         0),
    124 		(-1, 999999999,    999999999,   0, 999999998),
    125 		(-1, 999999999,   1000000000,   0, 999999999),
    126 		(-1, 999999999,   1000000001,   1,         0),
    127 		(-1, 999999999,   1999999999,   1, 999999998),
    128 		(-1, 999999999,   2000000000,   1, 999999999),
    129 		(-1, 999999999,   2000000001,   2,         0),
    130 
    131 		( 0, 999999999,  -2000000001,  -2, 999999998),
    132 		( 0, 999999999,  -2000000000,  -2, 999999999),
    133 		( 0, 999999999,  -1999999999,  -1,         0),
    134 		( 0, 999999999,  -1000000001,  -1, 999999998),
    135 		( 0, 999999999,  -1000000000,  -1, 999999999),
    136 		( 0, 999999999,   -999999999,   0,         0),
    137 		( 0, 999999999,           -1,   0, 999999998),
    138 		( 0, 999999999,            0,   0, 999999999),
    139 		( 0, 999999999,            1,   1,         0),
    140 		( 0, 999999999,    999999999,   1, 999999998),
    141 		( 0, 999999999,   1000000000,   1, 999999999),
    142 		( 0, 999999999,   1000000001,   2,         0),
    143 		( 0, 999999999,   1999999999,   2, 999999998),
    144 		( 0, 999999999,   2000000000,   2, 999999999),
    145 		( 0, 999999999,   2000000001,   3,         0),
    146 	];
    147 
    148 	for (let i = 0z; i < len(cases); i += 1) {
    149 		const C = cases[i];
    150 		const a = instant { sec = C.0, nsec = C.1 };
    151 		const x = C.2;
    152 		const b = instant { sec = C.3, nsec = C.4 };
    153 		const B = add(a, x);
    154 		assert(B.sec == b.sec, "time::add() .sec error");
    155 		assert(B.nsec == b.nsec, "time::add() .nsec error");
    156 	};
    157 };
    158 
    159 @test fn compare() void = {
    160 	let a = now(clock::MONOTONIC);
    161 	sleep(1 * MILLISECOND);
    162 	let b = now(clock::MONOTONIC);
    163 	assert(compare(a, b) < 0);
    164 	assert(compare(b, a) > 0);
    165 	assert(compare(a, a) == 0);
    166 };
    167 
    168 @test fn mult() void = {
    169 	const cases = [
    170 	//	instant a        factor f      instant b    interpretations
    171 		( 0,         0,  0.000,   0,         0), //  0.000000000
    172 		( 9,         0,  0.000,   0,         0), //  0.000000000
    173 		( 0, 999999999,  0.000,   0,         0), //  0.000000000
    174 		( 9, 999999999,  0.000,   0,         0), //  0.000000000
    175 
    176 		( 1,         0,  1.000,   1,         0), //  1.000000000
    177 		( 9,         0,  1.000,   9,         0), //  9.000000000
    178 		( 1, 999999999,  1.000,   1, 999999999), //  1.999999999
    179 		( 9, 999999999,  1.000,   9, 999999999), //  9.999999999
    180 
    181 		( 1,         0,  0.001,   0,   1000000), //  0.001000000
    182 		( 1,         0,  0.010,   0,  10000000), //  0.010000000
    183 		( 1,         0,  0.100,   0, 100000000), //  0.100000000
    184 
    185 		(-1,         0,  0.001,  -1, 999000000), // -0.001000000
    186 		(-1,         0,  0.010,  -1, 990000000), // -0.010000000
    187 		(-1,         0,  0.100,  -1, 900000000), // -0.100000000
    188 		(-1,         0,  1.000,  -1,         0), // -1.000000000
    189 
    190 		( 0, 500000000,  0.001,   0,    500000), //  0.005000000
    191 		( 0, 500000000,  0.010,   0,   5000000), //  0.050000000
    192 		( 0, 500000000,  0.100,   0,  50000000), //  0.500000000
    193 
    194 		( 2,         0,  0.001,   0,   2000000), //  0.002000000
    195 		( 2,         0,  0.010,   0,  20000000), //  0.020000000
    196 		( 2,         0,  0.100,   0, 200000000), //  0.200000000
    197 
    198 		( 3, 141592653,  3.141,   9, 867742523), //  9.867742523073
    199 		( 2, 718281828,  2.718,   7, 388290007), //  7.388290008504 (rounds down?)
    200 		( 1, 414213562,  1.414,   1, 999697975), //  1.999697976668 (rounds down?)
    201 
    202 		( 3, 141592653, -3.141, -10, 132257477), // -9.867742523073
    203 		( 2, 718281828, -2.718,  -8, 611709993), // -7.388290008504
    204 		( 1, 414213562, -1.414,  -2,    302025), // -1.999697976668
    205 
    206 		(-4, 858407347,  3.141, -10, 132257477), // -9.867742523073
    207 		(-3, 281718172,  2.718,  -8, 611709993), // -7.388290008504
    208 		(-2, 585786438,  1.414,  -2,    302025), // -1.999697976668
    209 
    210 		(-4, 858407347, -3.141,   9, 867742523), //  9.867742523073
    211 		(-3, 281718172, -2.718,   7, 388290007), //  7.388290008504
    212 		(-2, 585786438, -1.414,   1, 999697975), //  1.999697976668
    213 	];
    214 
    215 	for (let i = 0z; i < len(cases); i += 1) {
    216 		const C = cases[i];
    217 		const a = instant { sec = C.0, nsec = C.1 };
    218 		const f = C.2;
    219 		const b = instant { sec = C.3, nsec = C.4 };
    220 		const B = mult(a, f);
    221 		assert(B.sec == b.sec, "time::mult() .sec error");
    222 		assert(B.nsec == b.nsec, "time::mult() .nsec error");
    223 	};
    224 };