@@ -1172,27 +1172,30 @@ export class SQLExtracter {
1172
1172
}
1173
1173
}
1174
1174
1175
+ // A constant is represented by an empty Term object, where there is no indeterminate.
1176
+ const CONSTANT = '{}' ;
1177
+
1175
1178
export class Fraction {
1176
- num : Polynomial ;
1177
- den : Polynomial ;
1179
+ num : Multinomial ;
1180
+ den : Multinomial ;
1178
1181
1179
- constructor ( n ?: Polynomial , d ?: Polynomial ) {
1180
- this . num = n ? Polynomial . copyOf ( n ) : new Polynomial ( ) ;
1181
- this . den = d ? Polynomial . copyOf ( d ) : new Polynomial ( [ 1 ] ) ;
1182
+ constructor ( n ?: Multinomial , d ?: Multinomial ) {
1183
+ this . num = n ? Multinomial . copyOf ( n ) : new Multinomial ( ) ;
1184
+ this . den = d ? Multinomial . copyOf ( d ) : new Multinomial ( { [ CONSTANT ] : 1 } ) ;
1182
1185
if ( this . den . isZero ( ) ) {
1183
1186
throw new Error ( 'Division by zero.' ) ;
1184
1187
}
1185
1188
this . reduce ( ) ;
1186
1189
}
1187
1190
1188
1191
static add ( a : Fraction , b : Fraction ) : Fraction {
1189
- const den = Polynomial . multiply ( a . den , b . den ) ;
1190
- const num = Polynomial . add ( Polynomial . multiply ( a . num , b . den ) , Polynomial . multiply ( b . num , a . den ) ) ;
1192
+ const den = Multinomial . multiply ( a . den , b . den ) ;
1193
+ const num = Multinomial . add ( Multinomial . multiply ( a . num , b . den ) , Multinomial . multiply ( b . num , a . den ) ) ;
1191
1194
return new Fraction ( num , den ) ;
1192
1195
}
1193
1196
1194
1197
static negate ( a : Fraction ) : Fraction {
1195
- return new Fraction ( Polynomial . negate ( a . num ) , a . den ) ;
1198
+ return new Fraction ( Multinomial . negate ( a . num ) , a . den ) ;
1196
1199
}
1197
1200
1198
1201
static subtract ( a : Fraction , b : Fraction ) : Fraction {
@@ -1201,7 +1204,7 @@ export class Fraction {
1201
1204
}
1202
1205
1203
1206
static multiply ( a : Fraction , b : Fraction ) : Fraction {
1204
- return new Fraction ( Polynomial . multiply ( a . num , b . num ) , Polynomial . multiply ( a . den , b . den ) ) ;
1207
+ return new Fraction ( Multinomial . multiply ( a . num , b . num ) , Multinomial . multiply ( a . den , b . den ) ) ;
1205
1208
}
1206
1209
1207
1210
static divide ( a : Fraction , b : Fraction ) : Fraction {
@@ -1211,12 +1214,21 @@ export class Fraction {
1211
1214
1212
1215
reduce ( ) {
1213
1216
if ( this . num . isZero ( ) ) {
1214
- this . den = new Polynomial ( [ 1 ] ) ;
1217
+ this . den = new Multinomial ( { [ CONSTANT ] : 1 } ) ;
1215
1218
return ;
1216
1219
}
1217
- if ( this . num . isConstant ( ) && this . den . isConstant ( ) ) {
1218
- this . num = new Polynomial ( [ this . num . coeffs [ 0 ] / this . den . coeffs [ 0 ] ] ) ;
1219
- this . den = new Polynomial ( [ 1 ] ) ;
1220
+ if ( this . num . isConstant ( ) &&
1221
+ this . den . isConstant ( ) &&
1222
+ Number . isInteger ( this . num . terms [ CONSTANT ] ) &&
1223
+ Number . isInteger ( this . den . terms [ CONSTANT ] )
1224
+ ) {
1225
+ const gcd = ( a : number , b : number ) => {
1226
+ a = Math . abs ( a ) ; b = Math . abs ( b ) ;
1227
+ return b === 0 ? a : gcd ( b , a % b ) ;
1228
+ } ;
1229
+ const g = gcd ( this . num . terms [ CONSTANT ] , this . den . terms [ CONSTANT ] ) ;
1230
+ this . num = new Multinomial ( { [ CONSTANT ] : this . num . terms [ CONSTANT ] / g } ) ;
1231
+ this . den = new Multinomial ( { [ CONSTANT ] : this . den . terms [ CONSTANT ] / g } ) ;
1220
1232
return ;
1221
1233
}
1222
1234
// TODO(ragdev): Fractions can be reduced further by factoring out the gcd of
@@ -1234,9 +1246,10 @@ export class Fraction {
1234
1246
const fn = Fraction . fromExpression ( expr . expr ) ;
1235
1247
return Fraction . updateGivenOp ( expr . operator . op , [ fn ] ) ;
1236
1248
} else if ( expr instanceof FieldNamePrimitive && expr . evalType === Primitive . NUMBER ) {
1237
- return new Fraction ( new Polynomial ( [ 0 , 1 ] , expr . value ) ) ;
1249
+ const term = new Term ( { [ expr . value ] : 1 } ) ;
1250
+ return new Fraction ( new Multinomial ( { [ term . toKey ( ) ] : 1 } ) ) ;
1238
1251
} else if ( expr instanceof NumberPrimitive ) {
1239
- return new Fraction ( new Polynomial ( [ expr . value ] ) ) ;
1252
+ return new Fraction ( new Multinomial ( { [ CONSTANT ] : expr . value } ) ) ;
1240
1253
}
1241
1254
throw new Error ( `Cannot resolve expression: ${ expr . toString ( ) } ` ) ;
1242
1255
}
@@ -1254,143 +1267,219 @@ export class Fraction {
1254
1267
}
1255
1268
}
1256
1269
1257
- export class Polynomial {
1258
- private _coeffs : number [ ] ;
1259
- indeterminate ?: string ;
1270
+ export class Term {
1271
+ private _indeterminates : Dictionary < number > ;
1260
1272
1261
- constructor ( coeffs : number [ ] = [ 0 ] , variable ?: string ) {
1262
- this . coeffs = coeffs ;
1263
- this . indeterminate = variable ;
1273
+ constructor ( indeterminates : Dictionary < number > = { } ) {
1274
+ this . indeterminates = indeterminates ;
1264
1275
}
1265
1276
1266
- static copyOf ( pn : Polynomial ) : Polynomial {
1267
- return new Polynomial ( pn . coeffs , pn . indeterminate ) ;
1277
+ static copyOf ( tm : Term ) : Term {
1278
+ return new Term ( tm . indeterminates ) ;
1268
1279
}
1269
1280
1270
- get coeffs ( ) : number [ ] {
1271
- while ( this . _coeffs . length >= 1 && this . _coeffs [ this . _coeffs . length - 1 ] === 0 ) {
1272
- this . _coeffs . pop ( ) ;
1281
+ get indeterminates ( ) : Dictionary < number > {
1282
+ for ( const [ indeterminate , power ] of Object . entries ( this . _indeterminates ) ) {
1283
+ if ( power === 0 ) {
1284
+ delete this . _indeterminates [ indeterminate ] ;
1285
+ }
1273
1286
}
1274
- if ( this . _coeffs . length === 0 ) {
1275
- this . _coeffs . push ( 0 ) ;
1287
+ return this . _indeterminates ;
1288
+ }
1289
+
1290
+ set indeterminates ( indtrms : Dictionary < number > ) {
1291
+ this . _indeterminates = { ...indtrms } ;
1292
+ }
1293
+
1294
+ toKey ( ) : string {
1295
+ // sort the indeterminates
1296
+ const ordered = { } ;
1297
+ const unordered = this . indeterminates ;
1298
+ Object . keys ( unordered ) . sort ( ) . forEach ( ( key ) => {
1299
+ ordered [ key ] = unordered [ key ] ;
1300
+ } ) ;
1301
+ return JSON . stringify ( ordered ) ;
1302
+ }
1303
+
1304
+ static fromKey ( key : string ) : Term {
1305
+ return new Term ( JSON . parse ( key ) ) ;
1306
+ }
1307
+
1308
+ static indeterminateToExpression ( fn : string , pow : number ) : RefinementExpression {
1309
+ if ( pow <= 0 ) {
1310
+ throw new Error ( 'Must have positive power.' ) ;
1276
1311
}
1277
- return this . _coeffs ;
1312
+ if ( pow === 1 ) {
1313
+ return new FieldNamePrimitive ( fn , Primitive . NUMBER ) ;
1314
+ }
1315
+ return new BinaryExpression (
1316
+ Term . indeterminateToExpression ( fn , 1 ) ,
1317
+ Term . indeterminateToExpression ( fn , pow - 1 ) ,
1318
+ new RefinementOperator ( Op . MUL ) ) ;
1278
1319
}
1279
1320
1280
- set coeffs ( cfs : number [ ] ) {
1281
- this . _coeffs = [ ...cfs ] ;
1321
+ // assumes that term is not a constant i.e. not {}
1322
+ toExpression ( ) : RefinementExpression {
1323
+ if ( Object . keys ( this . indeterminates ) . length === 0 ) {
1324
+ throw new Error ( 'Cannot convert an empty term to expression' ) ;
1325
+ }
1326
+ let expr = null ;
1327
+ for ( const [ indeterminate , power ] of Object . entries ( this . indeterminates ) ) {
1328
+ const indtrExpr = Term . indeterminateToExpression ( indeterminate , power ) ;
1329
+ expr = expr ? new BinaryExpression ( expr , indtrExpr , new RefinementOperator ( Op . MUL ) ) : indtrExpr ;
1330
+ }
1331
+ return expr ;
1282
1332
}
1333
+ }
1283
1334
1284
- degree ( ) : number {
1285
- return this . coeffs . length - 1 ;
1335
+ export class Multinomial {
1336
+ private _terms : Dictionary < number > ;
1337
+
1338
+ constructor ( terms : Dictionary < number > = { } ) {
1339
+ this . terms = terms ;
1340
+ }
1341
+
1342
+ static copyOf ( mn : Multinomial ) : Multinomial {
1343
+ return new Multinomial ( mn . terms ) ;
1286
1344
}
1287
1345
1288
- static assertCompatibility ( a : Polynomial , b : Polynomial ) {
1289
- if ( a . indeterminate && b . indeterminate && a . indeterminate !== b . indeterminate ) {
1290
- throw new Error ( 'Incompatible polynomials' ) ;
1346
+ get terms ( ) : Dictionary < number > {
1347
+ for ( const [ term , coeff ] of Object . entries ( this . _terms ) ) {
1348
+ if ( coeff === 0 ) {
1349
+ delete this . _terms [ term ] ;
1350
+ }
1291
1351
}
1352
+ const ordered = { } ;
1353
+ const unordered = this . _terms ;
1354
+ Object . keys ( unordered ) . sort ( ) . forEach ( ( key ) => {
1355
+ ordered [ key ] = unordered [ key ] ;
1356
+ } ) ;
1357
+ this . _terms = ordered ;
1358
+ return this . _terms ;
1359
+ }
1360
+
1361
+ set terms ( tms : Dictionary < number > ) {
1362
+ this . _terms = { ...tms } ;
1292
1363
}
1293
1364
1294
- static add ( a : Polynomial , b : Polynomial ) : Polynomial {
1295
- Polynomial . assertCompatibility ( a , b ) ;
1296
- const sum = a . degree ( ) > b . degree ( ) ? Polynomial . copyOf ( a ) : Polynomial . copyOf ( b ) ;
1297
- const other = a . degree ( ) > b . degree ( ) ? b : a ;
1298
- for ( const [ i , coeff ] of other . coeffs . entries ( ) ) {
1299
- sum . coeffs [ i ] += coeff ;
1365
+ static add ( a : Multinomial , b : Multinomial ) : Multinomial {
1366
+ const sum = Multinomial . copyOf ( a ) ;
1367
+ for ( const [ term , coeff ] of Object . entries ( b . terms ) ) {
1368
+ const val = ( sum . terms [ term ] || 0 ) + coeff ;
1369
+ sum . terms [ term ] = val ;
1300
1370
}
1301
1371
return sum ;
1302
1372
}
1303
1373
1304
- static subtract ( a : Polynomial , b : Polynomial ) : Polynomial {
1305
- Polynomial . assertCompatibility ( a , b ) ;
1306
- return Polynomial . add ( a , Polynomial . negate ( b ) ) ;
1374
+ static subtract ( a : Multinomial , b : Multinomial ) : Multinomial {
1375
+ return Multinomial . add ( a , Multinomial . negate ( b ) ) ;
1307
1376
}
1308
1377
1309
- static negate ( a : Polynomial ) : Polynomial {
1310
- return new Polynomial ( a . coeffs . map ( co => - co ) , a . indeterminate ) ;
1378
+ static negate ( a : Multinomial ) : Multinomial {
1379
+ const neg = Multinomial . copyOf ( a ) ;
1380
+ for ( const [ term , coeff ] of Object . entries ( neg . terms ) ) {
1381
+ neg . terms [ term ] = - coeff ;
1382
+ }
1383
+ return neg ;
1311
1384
}
1312
1385
1313
- static multiply ( a : Polynomial , b : Polynomial ) : Polynomial {
1314
- Polynomial . assertCompatibility ( a , b ) ;
1315
- const deg = a . degree ( ) + b . degree ( ) ;
1316
- const coeffs = new Array ( deg + 1 ) . fill ( 0 ) ;
1317
- for ( let i = 0 ; i < coeffs . length ; i += 1 ) {
1318
- for ( let j = 0 ; j <= i ; j += 1 ) {
1319
- if ( j <= a . degree ( ) && ( i - j ) <= b . degree ( ) ) {
1320
- coeffs [ i ] += a . coeffs [ j ] * b . coeffs [ i - j ] ;
1386
+ static multiply ( a : Multinomial , b : Multinomial ) : Multinomial {
1387
+ const prod = new Multinomial ( ) ;
1388
+ for ( const [ aKey , acoeff ] of Object . entries ( a . terms ) ) {
1389
+ for ( const [ bKey , bcoeff ] of Object . entries ( b . terms ) ) {
1390
+ const tprod = Term . fromKey ( aKey ) ;
1391
+ const bterm = Term . fromKey ( bKey ) ;
1392
+ for ( const [ indeterminate , power ] of Object . entries ( bterm . indeterminates ) ) {
1393
+ const val = power + ( tprod . indeterminates [ indeterminate ] || 0 ) ;
1394
+ tprod . indeterminates [ indeterminate ] = val ;
1321
1395
}
1396
+ const val = acoeff * bcoeff + ( prod . terms [ tprod . toKey ( ) ] || 0 ) ;
1397
+ prod . terms [ tprod . toKey ( ) ] = val ;
1322
1398
}
1323
1399
}
1324
- return new Polynomial ( coeffs , a . indeterminate || b . indeterminate ) ;
1400
+ return prod ;
1325
1401
}
1326
1402
1327
1403
isZero ( ) : boolean {
1328
- return this . isConstant ( ) && this . coeffs [ 0 ] === 0 ;
1404
+ return Object . keys ( this . terms ) . length === 0 ;
1329
1405
}
1330
1406
1331
1407
isConstant ( ) : boolean {
1332
- return this . degree ( ) === 0 ;
1408
+ return this . isZero ( ) || ( Object . keys ( this . terms ) . length === 1 && this . terms . hasOwnProperty ( CONSTANT ) ) ;
1333
1409
}
1334
1410
1335
- static inderminateToExpression ( pow : number , fn : string ) : RefinementExpression {
1336
- if ( pow < 0 ) {
1337
- throw new Error ( 'Must have non-negative power.' ) ;
1338
- }
1339
- if ( pow === 0 ) {
1340
- return new NumberPrimitive ( 1 ) ;
1411
+ getIndeterminates ( ) : Set < string > {
1412
+ const indeterminates = new Set < string > ( ) ;
1413
+ for ( const tKey of Object . keys ( this . terms ) ) {
1414
+ const term = Term . fromKey ( tKey ) ;
1415
+ for ( const indeterminate of Object . keys ( term . indeterminates ) ) {
1416
+ indeterminates . add ( indeterminate ) ;
1417
+ }
1341
1418
}
1342
- if ( pow === 1 ) {
1343
- return new FieldNamePrimitive ( fn , Primitive . NUMBER ) ;
1419
+ return indeterminates ;
1420
+ }
1421
+
1422
+ isUnivariate ( ) : boolean {
1423
+ return this . getIndeterminates ( ) . size === 1 ;
1424
+ }
1425
+
1426
+ degree ( ) : number {
1427
+ let degree = 0 ;
1428
+ for ( const tKey of Object . keys ( this . terms ) ) {
1429
+ const term = Term . fromKey ( tKey ) ;
1430
+ let sum = 0 ;
1431
+ for ( const power of Object . values ( term . indeterminates ) ) {
1432
+ sum += power ;
1433
+ }
1434
+ degree = sum > degree ? sum : degree ;
1344
1435
}
1345
- return new BinaryExpression (
1346
- Polynomial . inderminateToExpression ( 1 , fn ) ,
1347
- Polynomial . inderminateToExpression ( pow - 1 , fn ) ,
1348
- new RefinementOperator ( Op . MUL ) ) ;
1436
+ return degree ;
1349
1437
}
1350
1438
1351
- // returns ax^n + bx^n-1 + ... c <op> 0
1439
+ // returns <multinomial> <op> CONSTANT
1352
1440
toExpression ( op : Op ) : RefinementExpression {
1353
- if ( this . degree ( ) === 0 ) {
1441
+ if ( this . isConstant ( ) ) {
1354
1442
return new BinaryExpression (
1355
- new NumberPrimitive ( this . coeffs [ 0 ] ) ,
1443
+ new NumberPrimitive ( this . isZero ( ) ? 0 : this . terms [ CONSTANT ] ) ,
1356
1444
new NumberPrimitive ( 0 ) ,
1357
1445
new RefinementOperator ( op ) ) ;
1358
1446
}
1359
- if ( ! this . indeterminate ) {
1360
- throw new Error ( 'FieldName not found!' ) ;
1361
- }
1362
- if ( this . degree ( ) === 1 ) {
1363
- // ax+b <op1> 0 => x <op2> -b/a
1447
+ if ( this . isUnivariate ( ) && this . degree ( ) === 1 ) {
1364
1448
const operator = new RefinementOperator ( op ) ;
1365
- if ( this . coeffs [ 1 ] < 0 ) {
1449
+ const indeterminate = this . getIndeterminates ( ) . values ( ) . next ( ) . value ;
1450
+ // TODO(ragdev): Implement a neater way to get the leading coefficient
1451
+ const leadingCoeff = this . terms [ `{"${ indeterminate } ":1}` ] ;
1452
+ const cnst = this . terms [ CONSTANT ] || 0 ;
1453
+ if ( leadingCoeff < 0 ) {
1366
1454
operator . flip ( ) ;
1367
1455
}
1368
1456
return new BinaryExpression (
1369
- new FieldNamePrimitive ( this . indeterminate , Primitive . NUMBER ) ,
1370
- new NumberPrimitive ( - this . coeffs [ 0 ] / this . coeffs [ 1 ] ) ,
1457
+ new FieldNamePrimitive ( indeterminate , Primitive . NUMBER ) ,
1458
+ new NumberPrimitive ( - cnst / leadingCoeff ) ,
1371
1459
operator ) ;
1372
1460
}
1373
- let expr = null ;
1374
- for ( let i = this . coeffs . length - 1 ; i >= 0 ; i -= 1 ) {
1375
- if ( this . coeffs [ i ] === 0 ) {
1376
- continue ;
1461
+ const termToExpression = ( tKey : string , tCoeff : number ) => {
1462
+ const termExpr = Term . fromKey ( tKey ) . toExpression ( ) ;
1463
+ if ( tCoeff === 1 ) {
1464
+ return termExpr ;
1377
1465
}
1378
- let termExpr = null ;
1379
- if ( i > 0 ) {
1380
- termExpr = Polynomial . inderminateToExpression ( i , this . indeterminate ) ;
1381
- if ( Math . abs ( this . coeffs [ i ] ) !== 1 ) {
1382
- termExpr = new BinaryExpression (
1383
- new NumberPrimitive ( this . coeffs [ i ] ) ,
1384
- termExpr ,
1385
- new RefinementOperator ( Op . MUL )
1386
- ) ;
1387
- }
1466
+ return new BinaryExpression (
1467
+ new NumberPrimitive ( tCoeff ) ,
1468
+ termExpr ,
1469
+ new RefinementOperator ( Op . MUL )
1470
+ ) ;
1471
+ } ;
1472
+ let expr = null ;
1473
+ let cnst = 0 ;
1474
+ for ( const [ tKey , tCoeff ] of Object . entries ( this . terms ) ) {
1475
+ if ( tKey === CONSTANT ) {
1476
+ cnst = tCoeff ;
1388
1477
} else {
1389
- termExpr = new NumberPrimitive ( this . coeffs [ 0 ] ) ;
1478
+ const termExpr = termToExpression ( tKey , tCoeff ) ;
1479
+ expr = expr ? new BinaryExpression ( expr , termExpr , new RefinementOperator ( Op . ADD ) ) : termExpr ;
1390
1480
}
1391
- expr = expr ? new BinaryExpression ( expr , termExpr , new RefinementOperator ( Op . ADD ) ) : termExpr ;
1392
1481
}
1393
- return new BinaryExpression ( expr , new NumberPrimitive ( 0 ) , new RefinementOperator ( op ) ) ;
1482
+ return new BinaryExpression ( expr , new NumberPrimitive ( - cnst ) , new RefinementOperator ( op ) ) ;
1394
1483
}
1395
1484
}
1396
1485
0 commit comments