index.js (13482B)
1 import isInteger from "lodash/isInteger.js"; 2 import { REArrayIndexNotFound, REDictPropertyNotFound, REOther, } from "../errors/messages.js"; 3 import * as DateTime from "../utility/DateTime.js"; 4 import { ImmutableMap } from "../utility/immutableMap.js"; 5 import { shuffle } from "../utility/E_A.js"; 6 import lodashIsEqual from "lodash/isEqual.js"; 7 class BaseValue { 8 clone() { 9 return Object.assign(Object.create(Object.getPrototypeOf(this)), this); 10 } 11 } 12 class VArray extends BaseValue { 13 constructor(value) { 14 super(); 15 this.value = value; 16 this.type = "Array"; 17 this.publicName = "List"; 18 } 19 toString() { 20 return "[" + this.value.map((v) => v.toString()).join(",") + "]"; 21 } 22 get(key) { 23 if (key.type === "Number") { 24 if (!isInteger(key.value)) { 25 throw new REArrayIndexNotFound("Array index must be an integer", key.value); 26 } 27 const index = key.value | 0; 28 if (index >= 0 && index < this.value.length) { 29 return this.value[index]; 30 } 31 else { 32 throw new REArrayIndexNotFound("Array index not found", index); 33 } 34 } 35 throw new REOther("Can't access non-numerical key on an array"); 36 } 37 flatten() { 38 return new VArray(this.value.reduce((acc, v) => acc.concat(v.type === "Array" ? v.value : [v]), [])); 39 } 40 shuffle() { 41 return new VArray(shuffle(this.value)); 42 } 43 isEqual(other) { 44 if (this.value.length !== other.value.length) { 45 return false; 46 } 47 for (let i = 0; i < this.value.length; i++) { 48 isEqual(this.value[i], other.value[i]); 49 } 50 return true; 51 } 52 } 53 export const vArray = (v) => new VArray(v); 54 class VBool extends BaseValue { 55 constructor(value) { 56 super(); 57 this.value = value; 58 this.type = "Bool"; 59 this.publicName = "Boolean"; 60 } 61 toString() { 62 return String(this.value); 63 } 64 isEqual(other) { 65 return this.value === other.value; 66 } 67 } 68 export const vBool = (v) => new VBool(v); 69 class VDate extends BaseValue { 70 constructor(value) { 71 super(); 72 this.value = value; 73 this.type = "Date"; 74 this.publicName = "Date"; 75 } 76 toString() { 77 return DateTime.Date.toString(this.value); 78 } 79 isEqual(other) { 80 return this.value === other.value; 81 } 82 } 83 export const vDate = (v) => new VDate(v); 84 class VDist extends BaseValue { 85 constructor(value) { 86 super(); 87 this.value = value; 88 this.type = "Dist"; 89 this.publicName = "Distribution"; 90 } 91 toString() { 92 return this.value.toString(); 93 } 94 isEqual(other) { 95 return this.value.isEqual(other.value); 96 } 97 } 98 export const vDist = (v) => new VDist(v); 99 class VLambda extends BaseValue { 100 constructor(value) { 101 super(); 102 this.value = value; 103 this.type = "Lambda"; 104 this.publicName = "Function"; 105 } 106 toString() { 107 return this.value.toString(); 108 } 109 get(key) { 110 if (key.type === "String" && key.value === "parameters") { 111 switch (this.value.type) { 112 case "UserDefinedLambda": 113 return vArray(this.value.parameters.map((parameter) => { 114 const fields = [ 115 ["name", vString(parameter.name)], 116 ]; 117 if (parameter.domain) { 118 fields.push(["domain", parameter.domain]); 119 } 120 return vDict(ImmutableMap(fields)); 121 })); 122 case "BuiltinLambda": 123 throw new REOther("Can't access parameters on built in functions"); 124 } 125 } 126 throw new REOther("No such field"); 127 } 128 } 129 export const vLambda = (v) => new VLambda(v); 130 class VNumber extends BaseValue { 131 constructor(value) { 132 super(); 133 this.value = value; 134 this.type = "Number"; 135 this.publicName = "Number"; 136 } 137 toString() { 138 return String(this.value); 139 } 140 isEqual(other) { 141 return this.value === other.value; 142 } 143 } 144 export const vNumber = (v) => new VNumber(v); 145 class VString extends BaseValue { 146 constructor(value) { 147 super(); 148 this.value = value; 149 this.type = "String"; 150 this.publicName = "String"; 151 } 152 toString() { 153 return JSON.stringify(this.value); 154 } 155 isEqual(other) { 156 return this.value === other.value; 157 } 158 } 159 export const vString = (v) => new VString(v); 160 class VDict extends BaseValue { 161 constructor(value) { 162 super(); 163 this.value = value; 164 this.type = "Dict"; 165 this.publicName = "Dictionary"; 166 } 167 toString() { 168 return ("{" + 169 [...this.value.entries()] 170 .map(([k, v]) => `${k}: ${v.toString()}`) 171 .join(",") + 172 "}"); 173 } 174 get(key) { 175 if (key.type === "String") { 176 const result = this.value.get(key.value); 177 if (!result) { 178 throw new REDictPropertyNotFound("Dict property not found", key.value); 179 } 180 return result; 181 } 182 else { 183 throw new REOther("Can't access non-string key on a dict"); 184 } 185 } 186 isEqual(other) { 187 if (this.value.size !== other.value.size) { 188 return false; 189 } 190 for (const [key, valueA] of this.value.entries()) { 191 const valueB = other.value.get(key); 192 if (!valueB) { 193 return false; 194 } 195 if (!isEqual(valueA, valueB)) { 196 return false; 197 } 198 } 199 return true; 200 } 201 } 202 export const vDict = (v) => new VDict(v); 203 class VTimeDuration extends BaseValue { 204 constructor(value) { 205 super(); 206 this.value = value; 207 this.type = "TimeDuration"; 208 this.publicName = "Time Duration"; 209 } 210 toString() { 211 return DateTime.Duration.toString(this.value); 212 } 213 isEqual(other) { 214 return this.value === other.value; 215 } 216 } 217 export const vTimeDuration = (v) => new VTimeDuration(v); 218 function scaleIsEqual(valueA, valueB) { 219 if (valueA.type !== valueB.type || 220 valueA.min !== valueB.min || 221 valueA.max !== valueB.max || 222 valueA.tickFormat !== valueB.tickFormat) { 223 return false; 224 } 225 switch (valueA.type) { 226 case "symlog": 227 return (valueA.constant === 228 valueB.constant); 229 case "power": 230 return (valueA.exponent === 231 valueB.exponent); 232 default: 233 return true; 234 } 235 } 236 export const SCALE_SYMLOG_DEFAULT_CONSTANT = 0.0001; 237 export const SCALE_POWER_DEFAULT_CONSTANT = 0.1; 238 class VScale extends BaseValue { 239 constructor(value) { 240 super(); 241 this.value = value; 242 this.type = "Scale"; 243 this.publicName = "Scale"; 244 } 245 toString() { 246 switch (this.value.type) { 247 case "linear": 248 return "Linear scale"; 249 case "log": 250 return "Logarithmic scale"; 251 case "symlog": 252 return `Symlog scale ({constant: ${this.value.constant || SCALE_SYMLOG_DEFAULT_CONSTANT}})`; 253 case "power": 254 return `Power scale ({exponent: ${this.value.exponent || SCALE_POWER_DEFAULT_CONSTANT}})`; 255 } 256 } 257 isEqual(other) { 258 return scaleIsEqual(this.value, other.value); 259 } 260 } 261 export const vScale = (scale) => new VScale(scale); 262 class VInput extends BaseValue { 263 constructor(value) { 264 super(); 265 this.value = value; 266 this.type = "Input"; 267 this.publicName = "Input"; 268 } 269 toString() { 270 switch (this.value.type) { 271 case "text": 272 return "Text input"; 273 case "textArea": 274 return "Text area input"; 275 case "checkbox": 276 return "Check box input"; 277 case "select": 278 return `Select input (${this.value.options.join(", ")})`; 279 } 280 } 281 isEqual(other) { 282 return lodashIsEqual(this.value, other.value); 283 } 284 } 285 export const vInput = (input) => new VInput(input); 286 class VTableChart extends BaseValue { 287 constructor(value) { 288 super(); 289 this.value = value; 290 this.type = "TableChart"; 291 this.publicName = "Table Chart"; 292 } 293 toString() { 294 return `Table with ${this.value.columns.length}x${this.value.data.length} elements`; 295 } 296 } 297 export const vTableChart = (v) => new VTableChart(v); 298 class VCalculator extends BaseValue { 299 constructor(value) { 300 super(); 301 this.value = value; 302 this.type = "Calculator"; 303 this.publicName = "Calculator"; 304 this.error = null; 305 if (!value.fn.parameterCounts().includes(value.inputs.length)) { 306 this.setError(`Calculator function needs ${value.fn.parameterCountString()} parameters, but ${value.inputs.length} fields were provided.`); 307 } 308 if (value.inputs.some((x) => x.name === "")) { 309 this.setError(`Calculator field names can't be empty.`); 310 } 311 const fieldNames = value.inputs.map((f) => f.name); 312 const uniqueNames = new Set(fieldNames); 313 if (fieldNames.length !== uniqueNames.size) { 314 this.setError(`Duplicate calculator field names found.`); 315 } 316 } 317 setError(message) { 318 this.error = new REOther(message); 319 } 320 getError() { 321 return this.error; 322 } 323 toString() { 324 return `Calculator`; 325 } 326 } 327 export const vCalculator = (v) => new VCalculator(v); 328 class VPlot extends BaseValue { 329 constructor(value) { 330 super(); 331 this.value = value; 332 this.type = "Plot"; 333 this.publicName = "Plot"; 334 } 335 toString() { 336 switch (this.value.type) { 337 case "distributions": 338 return `Plot containing ${this.value.distributions 339 .map((x) => x.name) 340 .join(", ")}`; 341 case "numericFn": 342 return `Plot for numeric function ${this.value.fn}`; 343 case "distFn": 344 return `Plot for dist function ${this.value.fn}`; 345 case "scatter": 346 return `Scatter plot for distributions ${this.value.xDist} and ${this.value.yDist}`; 347 case "relativeValues": 348 return `Plot for relative values ${this.value.ids.join(", ")}`; 349 } 350 } 351 get(key) { 352 if (key.type === "String" && 353 key.value === "fn" && 354 (this.value.type === "numericFn" || 355 this.value.type === "distFn" || 356 this.value.type === "relativeValues")) { 357 return vLambda(this.value.fn); 358 } 359 throw new REOther("Trying to access non-existent field"); 360 } 361 } 362 export const vPlot = (plot) => new VPlot(plot); 363 export class VDomain extends BaseValue { 364 constructor(value) { 365 super(); 366 this.value = value; 367 this.type = "Domain"; 368 this.publicName = "Domain"; 369 } 370 toString() { 371 return this.value.toString(); 372 } 373 get(key) { 374 if (key.type === "String" && this.value.type === "NumericRange") { 375 if (key.value === "min") { 376 return vNumber(this.value.min); 377 } 378 if (key.value === "max") { 379 return vNumber(this.value.max); 380 } 381 } 382 throw new REOther("Trying to access non-existent field"); 383 } 384 isEqual(other) { 385 return this.value.isEqual(other.value); 386 } 387 } 388 export const vDomain = (domain) => new VDomain(domain); 389 class VVoid extends BaseValue { 390 constructor() { 391 super(); 392 this.type = "Void"; 393 this.publicName = "Void"; 394 } 395 toString() { 396 return "()"; 397 } 398 } 399 export const vVoid = () => new VVoid(); 400 export function isEqual(a, b) { 401 if (a.type !== b.type) { 402 return false; 403 } 404 switch (a.type) { 405 case "Bool": 406 case "Number": 407 case "String": 408 case "Dist": 409 case "Date": 410 case "TimeDuration": 411 case "Scale": 412 case "Domain": 413 case "Array": 414 case "Dict": 415 return a.isEqual(b); 416 case "Void": 417 return true; 418 } 419 if (a.toString() !== b.toString()) { 420 return false; 421 } 422 throw new REOther("Equal not implemented for these inputs"); 423 } 424 const _isUniqableType = (t) => "isEqual" in t; 425 export function uniq(array) { 426 const uniqueArray = []; 427 for (const item of array) { 428 if (!_isUniqableType(item)) { 429 throw new REOther(`Can't apply uniq() to element with type ${item.type}`); 430 } 431 if (!uniqueArray.some((existingItem) => isEqual(existingItem, item))) { 432 uniqueArray.push(item); 433 } 434 } 435 return uniqueArray; 436 } 437 export function uniqBy(array, fn) { 438 const seen = []; 439 const uniqueArray = []; 440 for (const item of array) { 441 const computed = fn(item); 442 if (!_isUniqableType(computed)) { 443 throw new REOther(`Can't apply uniq() to element with type ${computed.type}`); 444 } 445 if (!seen.some((existingItem) => isEqual(existingItem, computed))) { 446 seen.push(computed); 447 uniqueArray.push(item); 448 } 449 } 450 return uniqueArray; 451 } 452 //# sourceMappingURL=index.js.map