Strongly type Forms on TaxReturn.
authorRobert Sesek <rsesek@bluestatic.org>
Sat, 22 Feb 2020 19:34:12 +0000 (14:34 -0500)
committerRobert Sesek <rsesek@bluestatic.org>
Sat, 22 Feb 2020 19:34:12 +0000 (14:34 -0500)
This lets type information be propagated for checking using
ReferenceLine and AccumulatorLine.

Two side effects of this change:

- The lines type is now exposed on Form, so that Line subclasses can use
  the type information.
- ReferenceLines to the same form have to erase the FormClass type.

src/Form.test.ts
src/Form.ts
src/Line.test.ts
src/Line.ts
src/TaxReturn.test.ts
src/TaxReturn.ts
src/fed2019/Form1040.ts
src/fed2019/Form8959.ts

index 122ca5d9b1b5d036e324f087536acc3ac1e9f730..c61ad583fced78f6eb120a07cdb99442eeddf26c 100644 (file)
@@ -1,6 +1,6 @@
 import { ComputedLine, Line } from './Line';
 import TaxReturn from './TaxReturn';
-import Form from './Form';
+import Form, { isFormT } from './Form';
 import { InconsistencyError, NotFoundError } from './Errors';
 
 test('add and get line', () => {
@@ -67,6 +67,22 @@ test('get value', () => {
   //expect(() => f.getValue(tr, 'other')).toThrow(NotFoundError);
 });
 
+test('form types', () => {
+  class FormA extends Form<any> {
+    readonly name = 'A';
+    protected readonly _lines = {};
+  };
+  class FormB extends Form<any> {
+    readonly name = 'B';
+    protected readonly _lines = {};
+  };
+
+  expect(isFormT(new FormA(), FormA)).toBe(true);
+  expect(isFormT(new FormB(), FormA)).toBe(false);
+  expect(isFormT(new FormA(), FormB)).toBe(false);
+  expect(isFormT(new FormB(), FormB)).toBe(true);
+});
+
 /*
 abstract class Form2<L extends { [key: string]: Line<any> } , I> {
   abstract readonly name: string;
index f438337480b59bd803cdb88a6f3093b33428ea42..7f2356d8fc63e0b7cd70134f59067408bdf9dc45 100644 (file)
@@ -12,6 +12,10 @@ export default abstract class Form<L extends { [key: string]: Line<any> },
 
   private readonly _input?: I;
 
+  // Avoid using this; prefer the getLine() helpers declared below. This
+  // is only exposed for propagating line type information.
+  get lines(): L { return this._lines; }
+
   constructor(input?: I) {
     this._input = input;
   }
@@ -42,3 +46,11 @@ export default abstract class Form<L extends { [key: string]: Line<any> },
     return this._input[name];
   }
 };
+
+export type FormClass<T extends Form<any>> = new (...args: any[]) => T;
+
+export function isFormT<T extends Form<any>>(form: Form<any>,
+                                             formClass: FormClass<T>):
+                                                form is T {
+  return form.constructor === formClass;
+}
index 7a2c531ff153e648bfeb04bae762a2503f38c37d..0014ce6137bf6b25f8ddc5ff0110e640322de87d 100644 (file)
@@ -1,5 +1,5 @@
 import { Line, AccumulatorLine, InputLine, ReferenceLine, ComputedLine } from './Line';
-import Form from './Form';
+import Form, { FormClass } from './Form';
 import TaxReturn from './TaxReturn';
 import { NotFoundError } from './Errors';
 
@@ -33,21 +33,54 @@ test('reference line', () => {
   class TestForm extends Form<TestForm['_lines']> {
     readonly name = 'Form 1';
     protected readonly _lines = {
-      '6b': new ConstantLine(12.34)
+      '6b': new ConstantLine(12.34),
+      's': new ConstantLine('abc'),
     };
   };
 
   const tr = new TaxReturn(2019);
   tr.addForm(new TestForm());
 
-  const l1 = new ReferenceLine<number>('Form 1', '6b');
-  expect(l1.value(tr)).toBe(12.34);
+  const l1 = new ReferenceLine(TestForm, '6b');
+  let n: number = l1.value(tr);
+  expect(n).toBe(12.34);
 
-  const l2 = new ReferenceLine<number>('Form 2', '6b');
-  expect(() => l2.value(tr)).toThrow(NotFoundError);
+  const l2 = new ReferenceLine(TestForm, 's');
+  let s: string = l2.value(tr);
+  expect(s).toBe('abc');
+
+  //TYPEERROR:
+  //const l3 = new ReferenceLine(TestForm, '7a');
+  //let n2: string = l1.value(tr);
+  //let s2: number = l2.value(tr);
+});
+
+test('self reference line', () => {
+  class OtherForm extends Form<OtherForm['_lines']> {
+    readonly name = 'Form A';
+    protected readonly _lines = {
+      '6c': new ConstantLine(55)
+    };
+  };
+  class TestForm extends Form<TestForm['_lines']> {
+    readonly name = 'Form 1';
+    protected readonly _lines = {
+      'a': new ConstantLine(100.2),
+      'b': new ReferenceLine(OtherForm, '6c'),
+      'c': new ReferenceLine((TestForm as unknown) as FormClass<Form<any>>, 'b'),
+      'd': new ReferenceLine(TestForm as any, 'b'),
+    };
+  };
+
+  const tr = new TaxReturn(2019);
+  const f = new TestForm();
+  tr.addForm(f);
+  tr.addForm(new OtherForm());
 
-  const l3 = new ReferenceLine<number>('Form 1', '7a');
-  expect(() => l3.value(tr)).toThrow(NotFoundError);
+  expect(f.getValue(tr, 'a')).toBe(100.2);
+  expect(f.getValue(tr, 'b')).toBe(55);
+  expect(f.getValue(tr, 'c')).toBe(55);
+  expect(f.getValue(tr, 'd')).toBe(55);
 });
 
 test('input line', () => {
@@ -85,7 +118,7 @@ test('line stack', () => {
     readonly name = 'Z-2';
     protected readonly _lines = {
       '2c': new ComputedLine<number>((tr: TaxReturn, l: Line<number>): any => {
-          return tr.getForm('Z').getLine('3').value(tr) * 0.2;
+          return tr.getForm(FormZ).getLine('3').value(tr) * 0.2;
         })
     };
   };
@@ -94,7 +127,7 @@ test('line stack', () => {
   tr.addForm(new FormZ({ 'input': 100 }));
   tr.addForm(new FormZ2());
 
-  const l = new ReferenceLine<number>('Z-2', '2c');
+  const l = new ReferenceLine(FormZ2, '2c');
   expect(l.value(tr)).toBe(20);
 });
 
@@ -112,6 +145,6 @@ test('accumulator line', () => {
   tr.addForm(new TestForm());
   tr.addForm(new TestForm());
 
-  const l = new AccumulatorLine('Form B', 'g');
+  const l = new AccumulatorLine(TestForm, 'g');
   expect(l.value(tr)).toBe(300.75);
 });
index 48141aec2168a7b560349dfb3dbf9ae9987e931c..e0e974a06c78c97711da86ada6a9a576c83cfabe 100644 (file)
@@ -1,5 +1,5 @@
 import TaxReturn from './TaxReturn';
-import Form from './Form';
+import Form, { FormClass } from './Form';
 
 export abstract class Line<T> {
   private _description?: string;
@@ -37,12 +37,18 @@ export class ComputedLine<T> extends Line<T> {
   }
 };
 
-export class ReferenceLine<T> extends Line<T> {
-  private _form: string;
-  private _line: string;
+export class ReferenceLine<F extends Form<any>,
+                           L extends keyof F['lines'],
+                           T extends ReturnType<F['lines'][L]['value']>>
+                               extends Line<T> {
+  private _form: FormClass<F>;
+  private _line: L;
   private _fallback?: T;
 
-  constructor(form: string, line: string, description?: string, fallback?: T) {
+  // If creating a ReferenceLine and F is the same class as the
+  // the one the Line is in, erase |form|'s type with |as any| to
+  // keep TypeScript happy.
+  constructor(form: FormClass<F>, line: L, description?: string, fallback?: T) {
     super(description || `Reference F${form}.L${line}`);
     this._form = form;
     this._line = line;
@@ -50,9 +56,11 @@ export class ReferenceLine<T> extends Line<T> {
   }
 
   value(tr: TaxReturn): T {
-    if (this._fallback !== undefined && !tr.maybeGetForm(this._form))
+    const form: F = tr.findForm(this._form);
+    if (this._fallback !== undefined && !form)
       return this._fallback;
-    return tr.getForm(this._form).getLine(this._line).value(tr);
+    const value: T = form.getValue(tr, this._line);
+    return value;
   }
 };
 
@@ -71,19 +79,21 @@ export class InputLine<U = unknown, T extends keyof U = any> extends Line<U[T]>
   }
 };
 
-export class AccumulatorLine extends Line<number> {
-  private _form: string;
-  private _line: string;
+export class AccumulatorLine<F extends Form<any>,
+                             L extends keyof F['lines']>
+                                 extends Line<number> {
+  private _form: FormClass<F>;
+  private _line: L;
 
-  constructor(form: string, line: string, description?: string) {
+  constructor(form: FormClass<F>, line: L, description?: string) {
     super(description || `Accumulator F${form}.L${line}`);
     this._form = form;
     this._line = line;
   }
 
   value(tr: TaxReturn): number {
-    const forms = tr.getForms(this._form);
-    const reducer = (acc: number, curr: Form<any>) => acc + (curr.getValue(tr, this._line) as number);
+    const forms: F[] = tr.findForms(this._form);
+    const reducer = (acc: number, curr: F) => acc + curr.getValue(tr, this._line);
     return forms.reduce(reducer, 0);
   }
 };
index 5914dcfb7f0bf3e68049ad839ca9eeb08e4d04c7..6adc7c64fc39dc3671336c34c4b7a1201623a96e 100644 (file)
@@ -62,7 +62,8 @@ test('single-copy forms', () => {
   const f = new TestForm();
   tr.addForm(f);
   expect(() => tr.addForm(new TestForm)).toThrow(InconsistencyError);
-  expect(tr.getForm(f.name)).toBe(f);
+  expect(tr.getForm(TestForm)).toBe(f);
+  expect(tr.findForm(TestForm)).toBe(f);
 });
 
 test('multiple-copy forms', () => {
@@ -79,9 +80,10 @@ test('multiple-copy forms', () => {
   tr.addForm(f1);
   tr.addForm(f2);
 
-  expect(() => tr.getForm(f1.name)).toThrow(InconsistencyError);
+  expect(() => tr.getForm(TestForm)).toThrow(InconsistencyError);
+  expect(() => tr.findForm(TestForm)).toThrow(InconsistencyError);
 
-  const forms = tr.getForms(f1.name);
+  const forms = tr.findForms(TestForm);
   expect(forms.length).toBe(2);
   expect(forms).toContain(f1);
   expect(forms).toContain(f2);
@@ -89,7 +91,64 @@ test('multiple-copy forms', () => {
 });
 
 test('get non-existent form', () => {
+  class TestForm extends Form<null> {
+    readonly name = 'Test Form';
+    protected readonly _lines = null;
+  }
   const tr = new TaxReturn(2019);
-  expect(() => tr.getForm('form')).toThrow(NotFoundError);
-  expect(tr.getForms('form')).toEqual([]);
+  expect(() => tr.getForm(TestForm)).toThrow(NotFoundError);
+  expect(tr.findForm(TestForm)).toBeNull();
+  expect(tr.findForms(TestForm)).toEqual([]);
+});
+
+type FormClass<T extends Form<any>> = Function & { prototype: T };
+
+class TR {
+  private _forms: Form<any>[] = [];
+
+  add(form: Form<any>) {
+    this._forms.push(form);
+  }
+
+  find(name: string): Form<any> {
+    const forms = this._forms.filter(f => f.name == name);
+    if (forms.length > 0)
+      return forms[0];
+    return null;
+  }
+
+  find2<T extends Form<any>>(cls: FormClass<T>): T[] {
+    let forms: T[] = [];
+    const isT = (form: Form<any>): form is T => form.constructor === cls;
+    for (let form of this._forms) {
+      if (isT(form))
+        forms.push(form);
+    }
+    return forms;
+  }
+};
+
+test('type test', () => {
+  class FormA extends Form<FormA['_lines']> {
+    readonly name = 'Form A';
+    protected readonly _lines = {};
+  };
+  class FormB extends Form<FormB['_lines']> {
+    readonly name = 'Form B';
+    readonly supportsMultipleCopies = true;
+    protected readonly _lines = {};
+  };
+
+  const tr = new TR();
+
+  tr.add(new FormA());
+  tr.add(new FormB());
+
+  expect(tr.find('Form A')).not.toBeNull();
+
+  expect(tr.find2(FormB).length).toBe(1);
+
+  tr.add(new FormB());
+  expect(tr.find2(FormB).length).toBe(2);
+
 });
index c8f6ca6c3fa881b932f8fcfad56b34196eeba0e5..b1d69ea3ca7ef366e528a5dcfa778b29c05bf69e 100644 (file)
@@ -1,4 +1,4 @@
-import Form from './Form';
+import Form, { FormClass, isFormT } from './Form';
 import Person, { Relation } from './Person';
 import { NotFoundError, InconsistencyError, UnsupportedFeatureError } from './Errors';
 
@@ -39,7 +39,7 @@ export default class TaxReturn {
 
   addForm(form: Form<any>) {
     if (!form.supportsMultipleCopies) {
-      const other = this.getForms(form.name);
+      const other = this.findForms(form.constructor as FormClass<Form<any>>);
       if (other.length > 0) {
         throw new InconsistencyError(`Cannot have more than one type of form ${form.name}`);
       }
@@ -48,25 +48,24 @@ export default class TaxReturn {
     this._forms.push(form);
   }
 
-  maybeGetForm<T extends Form<any>>(name: string): T | null {
-    const forms = this.getForms<T>(name);
-    if (forms.length == 0) {
+  findForm<T extends Form<any>>(cls: FormClass<T>): T | null {
+    const forms = this.findForms(cls);
+    if (forms.length == 0)
       return null;
-    }
-    if (forms.length > 1) {
-      throw new InconsistencyError(`More than 1 form named ${name}`);
-    }
+    if (forms.length > 1)
+      throw new InconsistencyError(`Form ${forms[0].name} has multiple copies`);
     return forms[0];
   }
 
-  getForm<T extends Form<any>>(name: string): T {
-    const form = this.maybeGetForm<T>(name);
-    if (!form)
-      throw new NotFoundError(`No form named ${name}`);
-    return form;
+  findForms<T extends Form<any>>(cls: FormClass<T>): T[] {
+    const forms: T[] = this._forms.filter((form: Form<any>): form is T => isFormT(form, cls));
+    return forms;
   }
 
-  getForms<T extends Form<any>>(name: string): T[] {
-    return this._forms.filter(f => f.name == name) as T[];
+  getForm<T extends Form<any>>(cls: FormClass<T>): T {
+    const form = this.findForm(cls);
+    if (!form)
+      throw new NotFoundError(`No form ${cls}`);
+    return form;
   }
 };
index a3ad3ae806ff351826923abccaeba5e86aa96814..f1106d86484e2f442fc7af360249de5411e475a6 100644 (file)
@@ -1,9 +1,12 @@
-import Form from '../Form';
+import Form, { FormClass } from '../Form';
 import TaxReturn from '../TaxReturn';
 import { Line, AccumulatorLine, ComputedLine, ReferenceLine } from '../Line';
 import { UnsupportedFeatureError } from '../Errors';
 
 import Form8959 from './Form8959';
+import Form1099INT from './Form1099INT';
+import Form1099DIV from './Form1099DIV';
+import FormW2 from './FormW2';
 
 export enum FilingStatus {
   Single,
@@ -21,26 +24,33 @@ export default class Form1040 extends Form<Form1040['_lines'], Form1040Input> {
   readonly name = '1040';
 
   protected readonly _lines = {
-    '1': new AccumulatorLine('W-2', '1', 'Wages, salaries, tips, etc.'),
-    '2a': new AccumulatorLine('1099-INT', '8', 'Tax-exempt interest'),
-    '2b': new AccumulatorLine('1099-INT', '1', 'Taxable interest'),
-    '3a': new AccumulatorLine('1099-DIV', '1b', 'Qualified dividends'),
-    '3b': new AccumulatorLine('1099-DIV', '1a', 'Ordinary dividends'),
+    '1': new AccumulatorLine(FormW2, '1', 'Wages, salaries, tips, etc.'),
+    '2a': new AccumulatorLine(Form1099INT, '8', 'Tax-exempt interest'),
+    '2b': new AccumulatorLine(Form1099INT, '1', 'Taxable interest'),
+    '3a': new AccumulatorLine(Form1099DIV, '1b', 'Qualified dividends'),
+    '3b': new AccumulatorLine(Form1099DIV, '1a', 'Ordinary dividends'),
     // 4a and 4b are complex
     '4b': new ComputedLine(() => 0),
     '4d': new ComputedLine(() => 0),
     // 4c and 4d are not supported
     // 5a and 5b are not supported
-    '6': new ReferenceLine<number>('Schedule D', '21', 'Capital gain/loss', 0),
-    '7a': new ReferenceLine<number>('Schedule 1', '9', 'Other income from Schedule 1', 0),
+    '6': new ReferenceLine(/*'Schedule D'*/ undefined, '21', 'Capital gain/loss', 0),
+    '7a': new ReferenceLine(/*'Schedule 1'*/ undefined, '9', 'Other income from Schedule 1', 0),
 
     '7b': new ComputedLine((tr: TaxReturn): number => {
-      const lineIds = ['1', '2b', '3b', '4b', '4d', /*'5b',*/ '6', '7a'];
-      const lines: number[] = lineIds.map(l => this.getValue(tr, l as keyof Form1040['_lines']));
-      return reduceBySum(lines);
+      let income = 0;
+      income += this.getValue(tr, '1');
+      income += this.getValue(tr, '2b');
+      income += this.getValue(tr, '3b');
+      income += this.getValue(tr, '4b');
+      income += this.getValue(tr, '4d');
+      //income += this.getValue(tr, '5b');
+      income += this.getValue(tr, '6');
+      income += this.getValue(tr, '7a');
+      return income;
     }, 'Total income'),
 
-    '8a': new ReferenceLine<number>('Schedule 1', '22', 'Adjustments to income', 0),
+    '8a': new ReferenceLine(undefined /*'Schedule 1'*/, '22', 'Adjustments to income', 0),
 
     '8b': new ComputedLine((tr: TaxReturn): number => {
       return this.getValue(tr, '7b') - this.getValue(tr, '8a');
@@ -113,7 +123,7 @@ export default class Form1040 extends Form<Form1040['_lines'], Form1040Input> {
     }, 'Tax'),
 
     '12b': new ComputedLine((tr: TaxReturn): number => {
-      return this.getValue(tr, '12a') + tr.getForm<Schedule2>('Schedule 2').getValue(tr, '3');
+      return this.getValue(tr, '12a') + tr.getForm(Schedule2).getValue(tr, '3');
     }, 'Additional tax'),
 
     // Not supported: 13a - child tax credit
@@ -130,7 +140,7 @@ export default class Form1040 extends Form<Form1040['_lines'], Form1040Input> {
       return value < 0 ? 0 : value;
     }),
 
-    '15': new ReferenceLine<number>('Schedule 2', '10', undefined, 0),
+    '15': new ReferenceLine(undefined /*'Schedule 2'*/, '10', undefined, 0),
 
     '16': new ComputedLine((tr: TaxReturn): number => {
       return this.getValue(tr, '14') + this.getValue(tr, '15');
@@ -138,12 +148,15 @@ export default class Form1040 extends Form<Form1040['_lines'], Form1040Input> {
 
     '17': new ComputedLine((tr: TaxReturn): number => {
       const fedTaxWithheldBoxes = [
-        ['W-2', '2'], ['1099-R', '4'], ['1099-DIV', '4'], ['1099-INT', '4']
+        new AccumulatorLine(FormW2, '2'),
+        //new AccumulatorLine(Form1099R, '4'),
+        new AccumulatorLine(Form1099DIV, '4'),
+        new AccumulatorLine(Form1099INT, '4'),
       ];
-      const withholding = fedTaxWithheldBoxes.map(b => (new AccumulatorLine(b[0], b[1])).value(tr));
+      const withholding: number[] = fedTaxWithheldBoxes.map(b => b.value(tr));
 
       let additionalMedicare = 0;
-      const f8959 = tr.maybeGetForm('8595')
+      const f8959 = tr.findForm(Form8959)
       if (f8959) {
         additionalMedicare = f8959.getValue(tr, '24');
       }
@@ -153,11 +166,11 @@ export default class Form1040 extends Form<Form1040['_lines'], Form1040Input> {
 
     // 18 not supported
 
-    '19': new ReferenceLine<number>('1040', '17', 'Total payments'),
+    '19': new ReferenceLine(Form1040 as any, '17', 'Total payments'),
 
     '20': new ComputedLine((tr: TaxReturn): number => {
-      const l16 = this.getValue(tr, '16');
-      const l19 = this.getValue(tr, '19');
+      const l16: number = this.getValue(tr, '16');
+      const l19: number = this.getValue(tr, '19');
       if (l19 > l16)
         return l19 - l16;
       return 0;
@@ -180,8 +193,9 @@ export class Schedule2 extends Form<Schedule2['_lines']> {
     '1': new ComputedLine((tr: TaxReturn): number => {
       // TODO - this is just using Taxable Income, rather than AMT-limited
       // income
-      const taxableIncome = tr.getForm('1040').getValue(tr, '11b');
-      switch (tr.getForm<Form1040>('1040').getInput('filingStatus')) {
+      const f1040 = tr.getForm(Form1040);
+      const taxableIncome = f1040.getValue(tr, '11b');
+      switch (f1040.getInput('filingStatus')) {
         case FilingStatus.Single:
           if (taxableIncome < 510300)
             return 0;
@@ -205,7 +219,7 @@ export class Schedule2 extends Form<Schedule2['_lines']> {
     // 6 is not supported (Additional tax on IRAs, other qualified retirement plans, and other tax-favored accounts)
     // 7 is not supported (Household employment taxes.)
     '8': new ComputedLine((tr: TaxReturn): number => {
-      const f1040 = tr.getForm<Form1040>('1040');
+      const f1040 = tr.getForm(Form1040);
       const wages = f1040.getLine('1').value(tr);
       const agi = f1040.getLine('8b').value(tr);
 
@@ -235,12 +249,12 @@ export class Schedule2 extends Form<Schedule2['_lines']> {
       let value = 0;
 
       if (additionalMedicare) {
-        const f8959 = tr.getForm('8959');
+        const f8959 = tr.getForm(Form8959);
         value += f8959.getValue(tr, '18');
       }
 
       if (niit) {
-        const f8960 = tr.getForm('8960');
+        //const f8960 = tr.getForm('8960');
       }
 
       return value;
index 2e01a0e14ccc8125ec2090c9aa7965d13a3c6dc1..93f7b433b228ace61d14e9f3e07af600d9006fe2 100644 (file)
@@ -3,12 +3,13 @@ import TaxReturn from '../TaxReturn';
 import { Line, AccumulatorLine, ComputedLine, ReferenceLine } from '../Line';
 
 import Form1040, { FilingStatus } from './Form1040';
+import FormW2 from './FormW2';
 
 export default class Form8959 extends Form<Form8959['_lines']> {
   readonly name = '8959';
 
   protected readonly _lines = {
-    '1': new AccumulatorLine('W-2', '5', 'Medicare wages'),
+    '1': new AccumulatorLine(FormW2, '5', 'Medicare wages'),
     // 2 is not supported (Unreported tips from Form 4137)
     // 3 is not supported (Wages from Form 8919)
     '4': new ComputedLine((tr: TaxReturn): number => {
@@ -16,7 +17,7 @@ export default class Form8959 extends Form<Form8959['_lines']> {
       return this.getValue(tr, '1');
     }),
     '5': new ComputedLine((tr: TaxReturn): number => {
-      return Form8959.filingStatusLimit(tr.getForm<Form1040>('1040').getInput('filingStatus'));
+      return Form8959.filingStatusLimit(tr.getForm(Form1040).getInput('filingStatus'));
     }),
     '6': new ComputedLine((tr: TaxReturn): number => {
       const value = this.getValue(tr, '5') - this.getValue(tr, '4');
@@ -33,8 +34,8 @@ export default class Form8959 extends Form<Form8959['_lines']> {
       return this.getValue(tr, '7');
     }),
 
-    '19': new AccumulatorLine('W-2', '6', 'Medicare tax withheld'),
-    '20': new ReferenceLine<number>('8595', '1'),
+    '19': new AccumulatorLine(FormW2, '6', 'Medicare tax withheld'),
+    '20': new ReferenceLine(Form8959 as any, '1'),
     '21': new ComputedLine((tr: TaxReturn): number => {
       return this.getValue(tr, '20') * 0.0145;
     }, 'Regular Medicare withholding on Medicare wages'),