(
ce: ComputeEngine,
name: string,
def: BoxedDefinition,
newDef:
| Partial<OperatorDefinition>
| BoxedOperatorDefinition
| Partial<ValueDefinition>
| BoxedValueDefinition
)
| 415 | * |
| 416 | * Without this, indexing into a computed list hid the unknown from the solver |
| 417 | * exactly as a value-bound symbol did — `Solve(At([Y, 2], 1) = 5, Y)` returned |
| 418 | * `[]`, i.e. "proven no solutions". |
| 419 | * |
| 420 | * Only a literal `List` with a literal integer index in range is reduced; |
| 421 | * indices are 1-based and a negative index counts from the end, matching `At`. |
| 422 | */ |
| 423 | export function reduceStructuralIndex(expr: Expression): Expression { |
| 424 | if (!isFunction(expr)) return expr; |
| 425 | |
| 426 | const ops = expr.ops; |
| 427 | const reduced = ops.map(reduceStructuralIndex); |
| 428 | const self = reduced.every((op, i) => op === ops[i]) |
| 429 | ? expr |
| 430 | : expr.engine.function(expr.operator, reduced); |
| 431 | |
| 432 | if (!isFunction(self, 'At') || self.nops !== 2) return self; |
| 433 | |
| 434 | const list = self.op1; |
| 435 | if (!isFunction(list, 'List')) return self; |
| 436 | |
| 437 | const index = self.op2; |
| 438 | if (!isNumber(index)) return self; |
| 439 | // A complex index (`1 + 2i`) is not a valid list position: decline rather |
| 440 | // than silently projecting on its real part. |
| 441 | if (index.im !== 0) return self; |
| 442 | const k = index.re; |
| 443 | if (!Number.isInteger(k) || k === 0) return self; |
| 444 | |
| 445 | const n = list.nops; |
| 446 | const i = k > 0 ? k : n + k + 1; |
| 447 | if (i < 1 || i > n) return self; |
| 448 |
no test coverage detected