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 };