function buildFileLogger(path, level = "info", maxFiles = undefined) {
  return function (config) {
    const filename = `${path}/${config.process.id}-${config.process.env}.log`;
    return winston.createLogger({
      format: winston.format.combine(
        winston.format.timestamp(),
        winston.format.json()
      ),
      level,
      levels: winston.config.syslog.levels,
      transports: [
        new winston.transports.File({
          filename,
          maxFiles,
        }),
      ],
    });
  };
}


function stringifyExtraMessagePropertiesForConsole(info) {
  const skippedProperties = ["message", "timestamp", "level"];
  let response = "";

  for (const key in info) {
    const value = info[key];
    if (skippedProperties.includes(key)) {
      continue;
    }
    if (value === undefined || value === null || value === "") {
      continue;
    }
    response += `${key}=${value} `;
  }

  return response;
}


(config) => {
    const loggers: ActionheroConfigLoggerBuilderArray = [];
    loggers.push(buildConsoleLogger(process.env.LOG_LEVEL));
    config.general.paths.log.forEach((p) => {
      loggers.push(buildFileLogger(p, process.env.LOG_LEVEL));
    });

    return {
      loggers,
      maxLogStringLength: 100, // the maximum length of param to log (we will truncate)
      maxLogArrayLength: 10, // the maximum number of items in an array to log before collapsing into one message
    };
  }


(p) => {
      loggers.push(buildFileLogger(p, process.env.LOG_LEVEL));
    }


(config) => {
    const loggers: ActionheroConfigLoggerBuilderArray = [];
    loggers.push(buildConsoleLogger("crit"));
    config.general.paths.log.forEach((p) => {
      loggers.push(buildFileLogger(p, "debug", 1));
    });

    return { loggers };
  }


(p) => {
      loggers.push(buildFileLogger(p, "debug", 1));
    }


(info) => {
          return `${info.timestamp} - ${info.level}: ${
            info.message
          } ${stringifyExtraMessagePropertiesForConsole(info)}`;
        }


/*
The loggers defined here will eventually be available via `import { loggers } from "actionhero"`

You may want to customize how Actionhero sets the log level.  By default, you can use `process.env.LOG_LEVEL` to change each logger's level (default: 'info')

learn more about winston v3 loggers @
 - https://github.com/winstonjs/winston
 - https://github.com/winstonjs/winston/blob/master/docs/transports.md
*/
type ActionheroConfigLoggerBuilderArray = Array<
  (config: any) => winston.Logger
>;


() =>
    createStyles({
        root: {
            height: '72px',
            fontSize: '16px',
        },
    })


(theme: Theme) => ({
    block: {
        padding: theme.spacing(2, 2),
        cursor: 'pointer',
        borderBottom: `1px solid ${theme.palette.custom.lightGray}`,
    },
    row: {
        display: 'flex',
        flexDirection: 'row',
    },
    arrow: {
        marginRight: '20px',
    },
    itemName: {
        fontSize: '16px',
    },
    formControl: {
        '& .MuiInput-underline:hover:not(.Mui-disabled):before': {
            borderColor: 'white',
        },
    },
    selectRoot: {
        fontSize: '16px',
        fontWeight: 700,
        borderBottom: `1px solid ${theme.palette.custom.lightGray}`,
        '&:before': {
            borderColor: 'white',
        },
        '&:after': {
            borderColor: 'white',
        },
        '&:focus-within .MuiSelect-standard': {
            backgroundColor: 'white',
        },
        '& .MuiInput-input': {
            paddingLeft: '16px',
            lineHeight: '66px',
        },
    },

    icon: {
        fill: `${colors.primary.main}`,
        marginRight: '10px',
    },
})


export class TiltLoader extends Loader {

	constructor( manager?: LoadingManager );

	load( url: string, onLoad: ( object: Group ) => void, onProgress?: ( event: ProgressEvent ) => void, onError?: ( event: ErrorEvent ) => void ): void;
	loadAsync( url: string, onProgress?: ( event: ProgressEvent ) => void ): Promise<Group>;
	parse( data: ArrayBuffer ): Group;

}


load( url: string, onLoad: ( object: Group ) => void, onProgress?: ( event: ProgressEvent ) => void, onError?: ( event: ErrorEvent ) => void ): void;


loadAsync( url: string, onProgress?: ( event: ProgressEvent ) => void ): Promise<Group>;


parse( data: ArrayBuffer ): Group;


/*
	returns [
		sum(x_i * x_i), sum(x_i * y_i), sum(x_i * z_i),
		sum(y_i * x_i), sum(y_i * y_i), sum(y_i * z_i),
		sum(z_i * x_i), sum(z_i * y_i), sum(z_i * z_i)
	]
*/
function transformSqrdComponentsByMatrix3x3(matrix: Matrix3x3, sqrd: Vector3): Matrix3x3 {
	const m = matrix;
	const x2 = (m[0])**2 * sqrd[0] + (m[3])**2 * sqrd[1] + (m[6])**2 * sqrd[2];
	const y2 = (m[1])**2 * sqrd[0] + (m[4])**2 * sqrd[1] + (m[7])**2 * sqrd[2];
	const z2 = (m[2])**2 * sqrd[0] + (m[5])**2 * sqrd[1] + (m[8])**2 * sqrd[2];
	const xy = m[0] * m[1] * sqrd[0] + m[3] * m[4] * sqrd[1] + m[6] * m[7] * sqrd[2];
	const yz = m[1] * m[2] * sqrd[0] + m[4] * m[5] * sqrd[1] + m[7] * m[8] * sqrd[2];
	const zx = m[2] * m[0] * sqrd[0] + m[5] * m[3] * sqrd[1] + m[8] * m[6] * sqrd[2];
	return [
		x2, xy, zx,
		xy, y2, yz,
		zx, yz, z2
	]
}


/*
	sum means the sum over all particles
	sumsOfSingles: [ sum(x_i), sum(y_i), sum(z_i) ]
	sumsOfPairs: [
		sum(x_i * x_i), sum(x_i * y_i), sum(x_i * z_i),
		sum(y_i * x_i), sum(y_i * y_i), sum(y_i * z_i),
		sum(z_i * x_i), sum(z_i * y_i), sum(z_i * z_i)
	]
*/
function calculateTranslatedInertiaTensorComponents(
	sumsOfPairs: number[], mass: number, translation: Vector3): number[] {

	const [tx, ty, tz] = translation;
	return [
		sumsOfPairs[0] + mass * tx**2,
		sumsOfPairs[4] + mass * ty**2,
		sumsOfPairs[8] + mass * tz**2,
		sumsOfPairs[1] + mass * tx * ty,
		sumsOfPairs[5] + mass * ty * tz,
		sumsOfPairs[6] + mass * tz * tx
	]
}


/*
	components: [
		x^2, y^2, z^2,
		x*y, y*z, z*x
	]
*/
function createInertiaTensorFromComponents(components: number[]): Matrix3x3 {
	const c = components;
	return [
		c[1] + c[2], -c[3], 	   -c[5],
		-c[3], 		  c[2] + c[0], -c[4],
		-c[5], 		 -c[4], 		c[0] + c[1]
	]
}


export function calculateSqrdComponentsFromInertiaVector(inertiaVector: Vector3): Vector3 {
	const sqrdSum = (inertiaVector[0] + inertiaVector[1] + inertiaVector[2]) / 2;
	return subtract([sqrdSum, sqrdSum, sqrdSum], inertiaVector);
}


export function transformInertiaVector(transform: Transform, inertiaVector: Vector3, mass: number): Matrix3x3 {
	const sqrd = calculateSqrdComponentsFromInertiaVector(inertiaVector);
	const rotatedComponents = transformSqrdComponentsByMatrix3x3(transform.orientation, sqrd);
	const translatedComponents = calculateTranslatedInertiaTensorComponents(rotatedComponents, mass, transform.position);
	return createInertiaTensorFromComponents(translatedComponents);
}


type Matrix3x3 = Matrix3;


() => {
    it("can be constructed with default fees", async () => {
      pendingWithoutSimapp();
      const wallet = await DirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic);
      const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet);
      const openedClient = (client as unknown) as PrivateSigningStargateClient;
      expect(openedClient.fees).toEqual({
        send: {
          amount: [
            {
              amount: "2000",
              denom: "ucosm",
            },
          ],
          gas: "80000",
        },
        delegate: {
          amount: [
            {
              amount: "4000",
              denom: "ucosm",
            },
          ],
          gas: "160000",
        },
        transfer: {
          amount: [
            {
              amount: "4000",
              denom: "ucosm",
            },
          ],
          gas: "160000",
        },
        undelegate: {
          amount: [
            {
              amount: "4000",
              denom: "ucosm",
            },
          ],
          gas: "160000",
        },
        withdraw: {
          amount: [
            {
              amount: "4000",
              denom: "ucosm",
            },
          ],
          gas: "160000",
        },
      });
    });

    it("can be constructed with custom registry", async () => {
      pendingWithoutSimapp();
      const wallet = await DirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic);
      const registry = new Registry();
      registry.register("/custom.MsgCustom", MsgSend);
      const options = { registry: registry };
      const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet, options);
      const openedClient = (client as unknown) as PrivateSigningStargateClient;
      expect(openedClient.registry.lookupType("/custom.MsgCustom")).toEqual(MsgSend);
    });

    it("can be constructed with custom gas price", async () => {
      pendingWithoutSimapp();
      const wallet = await DirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic);
      const gasPrice = GasPrice.fromString("3.14utest");
      const options = { gasPrice: gasPrice };
      const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet, options);
      const openedClient = (client as unknown) as PrivateSigningStargateClient;
      expect(openedClient.fees).toEqual({
        send: {
          amount: [
            {
              amount: "251200", // 3.14 * 80_000
              denom: "utest",
            },
          ],
          gas: "80000",
        },
        delegate: {
          amount: [
            {
              amount: "502400", // 3.14 * 160_000
              denom: "utest",
            },
          ],
          gas: "160000",
        },
        transfer: {
          amount: [
            {
              amount: "502400",
              denom: "utest",
            },
          ],
          gas: "160000",
        },
        undelegate: {
          amount: [
            {
              amount: "502400",
              denom: "utest",
            },
          ],
          gas: "160000",
        },
        withdraw: {
          amount: [
            {
              amount: "502400",
              denom: "utest",
            },
          ],
          gas: "160000",
        },
      });
    });

    it("can be constructed with custom gas limits", async () => {
      pendingWithoutSimapp();
      const wallet = await DirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic);
      const gasLimits = {
        send: 160000,
        delegate: 120000,
      };
      const options = { gasLimits: gasLimits };
      const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet, options);
      const openedClient = (client as unknown) as PrivateSigningStargateClient;
      expect(openedClient.fees).toEqual({
        send: {
          amount: [
            {
              amount: "4000", // 0.025 * 160_000
              denom: "ucosm",
            },
          ],
          gas: "160000",
        },
        delegate: {
          amount: [
            {
              amount: "3000", // 0.025 * 120_000
              denom: "ucosm",
            },
          ],
          gas: "120000",
        },
        transfer: {
          amount: [
            {
              amount: "4000",
              denom: "ucosm",
            },
          ],
          gas: "160000",
        },
        undelegate: {
          amount: [
            {
              amount: "4000",
              denom: "ucosm",
            },
          ],
          gas: "160000",
        },
        withdraw: {
          amount: [
            {
              amount: "4000",
              denom: "ucosm",
            },
          ],
          gas: "160000",
        },
      });
    });

    it("can be constructed with custom gas price and gas limits", async () => {
      pendingWithoutSimapp();
      const wallet = await DirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic);
      const gasPrice = GasPrice.fromString("3.14utest");
      const gasLimits = {
        send: 160000,
      };
      const options = { gasPrice: gasPrice, gasLimits: gasLimits };
      const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet, options);
      const openedClient = (client as unknown) as PrivateSigningStargateClient;
      expect(openedClient.fees).toEqual({
        send: {
          amount: [
            {
              amount: "502400", // 3.14 * 160_000
              denom: "utest",
            },
          ],
          gas: "160000",
        },
        delegate: {
          amount: [
            {
              amount: "502400", // 3.14 * 160_000
              denom: "utest",
            },
          ],
          gas: "160000",
        },
        transfer: {
          amount: [
            {
              amount: "502400",
              denom: "utest",
            },
          ],
          gas: "160000",
        },
        undelegate: {
          amount: [
            {
              amount: "502400",
              denom: "utest",
            },
          ],
          gas: "160000",
        },
        withdraw: {
          amount: [
            {
              amount: "502400",
              denom: "utest",
            },
          ],
          gas: "160000",
        },
      });
    });
  }


async () => {
      pendingWithoutSimapp();
      const wallet = await DirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic);
      const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet);
      const openedClient = (client as unknown) as PrivateSigningStargateClient;
      expect(openedClient.fees).toEqual({
        send: {
          amount: [
            {
              amount: "2000",
              denom: "ucosm",
            },
          ],
          gas: "80000",
        },
        delegate: {
          amount: [
            {
              amount: "4000",
              denom: "ucosm",
            },
          ],
          gas: "160000",
        },
        transfer: {
          amount: [
            {
              amount: "4000",
              denom: "ucosm",
            },
          ],
          gas: "160000",
        },
        undelegate: {
          amount: [
            {
              amount: "4000",
              denom: "ucosm",
            },
          ],
          gas: "160000",
        },
        withdraw: {
          amount: [
            {
              amount: "4000",
              denom: "ucosm",
            },
          ],
          gas: "160000",
        },
      });
    }


async () => {
      pendingWithoutSimapp();
      const wallet = await DirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic);
      const registry = new Registry();
      registry.register("/custom.MsgCustom", MsgSend);
      const options = { registry: registry };
      const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet, options);
      const openedClient = (client as unknown) as PrivateSigningStargateClient;
      expect(openedClient.registry.lookupType("/custom.MsgCustom")).toEqual(MsgSend);
    }


async () => {
      pendingWithoutSimapp();
      const wallet = await DirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic);
      const gasPrice = GasPrice.fromString("3.14utest");
      const options = { gasPrice: gasPrice };
      const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet, options);
      const openedClient = (client as unknown) as PrivateSigningStargateClient;
      expect(openedClient.fees).toEqual({
        send: {
          amount: [
            {
              amount: "251200", // 3.14 * 80_000
              denom: "utest",
            },
          ],
          gas: "80000",
        },
        delegate: {
          amount: [
            {
              amount: "502400", // 3.14 * 160_000
              denom: "utest",
            },
          ],
          gas: "160000",
        },
        transfer: {
          amount: [
            {
              amount: "502400",
              denom: "utest",
            },
          ],
          gas: "160000",
        },
        undelegate: {
          amount: [
            {
              amount: "502400",
              denom: "utest",
            },
          ],
          gas: "160000",
        },
        withdraw: {
          amount: [
            {
              amount: "502400",
              denom: "utest",
            },
          ],
          gas: "160000",
        },
      });
    }


async () => {
      pendingWithoutSimapp();
      const wallet = await DirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic);
      const gasLimits = {
        send: 160000,
        delegate: 120000,
      };
      const options = { gasLimits: gasLimits };
      const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet, options);
      const openedClient = (client as unknown) as PrivateSigningStargateClient;
      expect(openedClient.fees).toEqual({
        send: {
          amount: [
            {
              amount: "4000", // 0.025 * 160_000
              denom: "ucosm",
            },
          ],
          gas: "160000",
        },
        delegate: {
          amount: [
            {
              amount: "3000", // 0.025 * 120_000
              denom: "ucosm",
            },
          ],
          gas: "120000",
        },
        transfer: {
          amount: [
            {
              amount: "4000",
              denom: "ucosm",
            },
          ],
          gas: "160000",
        },
        undelegate: {
          amount: [
            {
              amount: "4000",
              denom: "ucosm",
            },
          ],
          gas: "160000",
        },
        withdraw: {
          amount: [
            {
              amount: "4000",
              denom: "ucosm",
            },
          ],
          gas: "160000",
        },
      });
    }


async () => {
      pendingWithoutSimapp();
      const wallet = await DirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic);
      const gasPrice = GasPrice.fromString("3.14utest");
      const gasLimits = {
        send: 160000,
      };
      const options = { gasPrice: gasPrice, gasLimits: gasLimits };
      const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet, options);
      const openedClient = (client as unknown) as PrivateSigningStargateClient;
      expect(openedClient.fees).toEqual({
        send: {
          amount: [
            {
              amount: "502400", // 3.14 * 160_000
              denom: "utest",
            },
          ],
          gas: "160000",
        },
        delegate: {
          amount: [
            {
              amount: "502400", // 3.14 * 160_000
              denom: "utest",
            },
          ],
          gas: "160000",
        },
        transfer: {
          amount: [
            {
              amount: "502400",
              denom: "utest",
            },
          ],
          gas: "160000",
        },
        undelegate: {
          amount: [
            {
              amount: "502400",
              denom: "utest",
            },
          ],
          gas: "160000",
        },
        withdraw: {
          amount: [
            {
              amount: "502400",
              denom: "utest",
            },
          ],
          gas: "160000",
        },
      });
    }


() => {
    it("works with direct signer", async () => {
      pendingWithoutSimapp();
      const wallet = await DirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic);
      const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet);

      const transferAmount = coins(7890, "ucosm");
      const beneficiaryAddress = makeRandomAddress();
      const memo = "for dinner";

      // no tokens here
      const before = await client.getBalance(beneficiaryAddress, "ucosm");
      expect(before).toEqual({
        denom: "ucosm",
        amount: "0",
      });

      // send
      const result = await client.sendTokens(faucet.address0, beneficiaryAddress, transferAmount, memo);
      assertIsBroadcastTxSuccess(result);
      expect(result.rawLog).toBeTruthy();

      // got tokens
      const after = await client.getBalance(beneficiaryAddress, "ucosm");
      expect(after).toEqual(transferAmount[0]);
    });

    it("works with legacy Amino signer", async () => {
      pendingWithoutSimapp();
      const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);
      const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet);

      const transferAmount = coins(7890, "ucosm");
      const beneficiaryAddress = makeRandomAddress();
      const memo = "for dinner";

      // no tokens here
      const before = await client.getBalance(beneficiaryAddress, "ucosm");
      expect(before).toEqual({
        denom: "ucosm",
        amount: "0",
      });

      // send
      const result = await client.sendTokens(faucet.address0, beneficiaryAddress, transferAmount, memo);
      assertIsBroadcastTxSuccess(result);
      expect(result.rawLog).toBeTruthy();

      // got tokens
      const after = await client.getBalance(beneficiaryAddress, "ucosm");
      expect(after).toEqual(transferAmount[0]);
    });
  }


async () => {
      pendingWithoutSimapp();
      const wallet = await DirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic);
      const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet);

      const transferAmount = coins(7890, "ucosm");
      const beneficiaryAddress = makeRandomAddress();
      const memo = "for dinner";

      // no tokens here
      const before = await client.getBalance(beneficiaryAddress, "ucosm");
      expect(before).toEqual({
        denom: "ucosm",
        amount: "0",
      });

      // send
      const result = await client.sendTokens(faucet.address0, beneficiaryAddress, transferAmount, memo);
      assertIsBroadcastTxSuccess(result);
      expect(result.rawLog).toBeTruthy();

      // got tokens
      const after = await client.getBalance(beneficiaryAddress, "ucosm");
      expect(after).toEqual(transferAmount[0]);
    }


async () => {
      pendingWithoutSimapp();
      const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);
      const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet);

      const transferAmount = coins(7890, "ucosm");
      const beneficiaryAddress = makeRandomAddress();
      const memo = "for dinner";

      // no tokens here
      const before = await client.getBalance(beneficiaryAddress, "ucosm");
      expect(before).toEqual({
        denom: "ucosm",
        amount: "0",
      });

      // send
      const result = await client.sendTokens(faucet.address0, beneficiaryAddress, transferAmount, memo);
      assertIsBroadcastTxSuccess(result);
      expect(result.rawLog).toBeTruthy();

      // got tokens
      const after = await client.getBalance(beneficiaryAddress, "ucosm");
      expect(after).toEqual(transferAmount[0]);
    }


() => {
      it("works", async () => {
        pendingWithoutSimapp();
        const wallet = await DirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic);
        const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet);

        const msg = MsgDelegate.fromPartial({
          delegatorAddress: faucet.address0,
          validatorAddress: validator.validatorAddress,
          amount: coin(1234, "ustake"),
        });
        const msgAny: MsgDelegateEncodeObject = {
          typeUrl: "/cosmos.staking.v1beta1.MsgDelegate",
          value: msg,
        };
        const fee = {
          amount: coins(2000, "ucosm"),
          gas: "180000", // 180k
        };
        const memo = "Use your power wisely";
        const result = await client.signAndBroadcast(faucet.address0, [msgAny], fee, memo);
        assertIsBroadcastTxSuccess(result);
      });

      it("works with a modifying signer", async () => {
        pendingWithoutSimapp();
        const wallet = await ModifyingDirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic);
        const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet);

        const msg = MsgDelegate.fromPartial({
          delegatorAddress: faucet.address0,
          validatorAddress: validator.validatorAddress,
          amount: coin(1234, "ustake"),
        });
        const msgAny: MsgDelegateEncodeObject = {
          typeUrl: "/cosmos.staking.v1beta1.MsgDelegate",
          value: msg,
        };
        const fee = {
          amount: coins(2000, "ucosm"),
          gas: "180000", // 180k
        };
        const memo = "Use your power wisely";
        const result = await client.signAndBroadcast(faucet.address0, [msgAny], fee, memo);
        assertIsBroadcastTxSuccess(result);

        await sleep(1000);

        const searchResult = await client.getTx(result.transactionHash);
        assert(searchResult, "Must find transaction");
        const tx = decodeTxRaw(searchResult.tx);
        // From ModifyingDirectSecp256k1HdWallet
        expect(tx.body.memo).toEqual("This was modified");
        expect({ ...tx.authInfo.fee!.amount[0] }).toEqual(coin(3000, "ucosm"));
        expect(tx.authInfo.fee!.gasLimit.toNumber()).toEqual(333333);
      });
    }


async () => {
        pendingWithoutSimapp();
        const wallet = await DirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic);
        const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet);

        const msg = MsgDelegate.fromPartial({
          delegatorAddress: faucet.address0,
          validatorAddress: validator.validatorAddress,
          amount: coin(1234, "ustake"),
        });
        const msgAny: MsgDelegateEncodeObject = {
          typeUrl: "/cosmos.staking.v1beta1.MsgDelegate",
          value: msg,
        };
        const fee = {
          amount: coins(2000, "ucosm"),
          gas: "180000", // 180k
        };
        const memo = "Use your power wisely";
        const result = await client.signAndBroadcast(faucet.address0, [msgAny], fee, memo);
        assertIsBroadcastTxSuccess(result);
      }


async () => {
        pendingWithoutSimapp();
        const wallet = await ModifyingDirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic);
        const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet);

        const msg = MsgDelegate.fromPartial({
          delegatorAddress: faucet.address0,
          validatorAddress: validator.validatorAddress,
          amount: coin(1234, "ustake"),
        });
        const msgAny: MsgDelegateEncodeObject = {
          typeUrl: "/cosmos.staking.v1beta1.MsgDelegate",
          value: msg,
        };
        const fee = {
          amount: coins(2000, "ucosm"),
          gas: "180000", // 180k
        };
        const memo = "Use your power wisely";
        const result = await client.signAndBroadcast(faucet.address0, [msgAny], fee, memo);
        assertIsBroadcastTxSuccess(result);

        await sleep(1000);

        const searchResult = await client.getTx(result.transactionHash);
        assert(searchResult, "Must find transaction");
        const tx = decodeTxRaw(searchResult.tx);
        // From ModifyingDirectSecp256k1HdWallet
        expect(tx.body.memo).toEqual("This was modified");
        expect({ ...tx.authInfo.fee!.amount[0] }).toEqual(coin(3000, "ucosm"));
        expect(tx.authInfo.fee!.gasLimit.toNumber()).toEqual(333333);
      }


async () => {
        pendingWithoutSimapp();
        const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);
        const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet);

        const msgSend: MsgSend = {
          fromAddress: faucet.address0,
          toAddress: makeRandomAddress(),
          amount: coins(1234, "ucosm"),
        };
        const msgAny: MsgSendEncodeObject = {
          typeUrl: "/cosmos.bank.v1beta1.MsgSend",
          value: msgSend,
        };
        const fee = {
          amount: coins(2000, "ucosm"),
          gas: "200000",
        };
        const memo = "Use your tokens wisely";
        const result = await client.signAndBroadcast(faucet.address0, [msgAny], fee, memo);
        assertIsBroadcastTxSuccess(result);
      }


async () => {
        pendingWithoutSimapp();
        const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);
        const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet);

        const msgDelegate: MsgDelegate = {
          delegatorAddress: faucet.address0,
          validatorAddress: validator.validatorAddress,
          amount: coin(1234, "ustake"),
        };
        const msgAny: MsgDelegateEncodeObject = {
          typeUrl: "/cosmos.staking.v1beta1.MsgDelegate",
          value: msgDelegate,
        };
        const fee = {
          amount: coins(2000, "ustake"),
          gas: "200000",
        };
        const memo = "Use your tokens wisely";
        const result = await client.signAndBroadcast(faucet.address0, [msgAny], fee, memo);
        assertIsBroadcastTxSuccess(result);
      }


async () => {
        pendingWithoutSimapp();
        const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);

        const customRegistry = new Registry();
        const msgDelegateTypeUrl = "/cosmos.staking.v1beta1.MsgDelegate";
        interface CustomMsgDelegate {
          customDelegatorAddress?: string;
          customValidatorAddress?: string;
          customAmount?: Coin;
        }
        const baseCustomMsgDelegate: CustomMsgDelegate = {
          customDelegatorAddress: "",
          customValidatorAddress: "",
        };
        const CustomMsgDelegate = {
          // Adapted from autogenerated MsgDelegate implementation
          encode(
            message: CustomMsgDelegate,
            writer: protobuf.Writer = protobuf.Writer.create(),
          ): protobuf.Writer {
            writer.uint32(10).string(message.customDelegatorAddress ?? "");
            writer.uint32(18).string(message.customValidatorAddress ?? "");
            if (message.customAmount !== undefined && message.customAmount !== undefined) {
              Coin.encode(message.customAmount, writer.uint32(26).fork()).ldelim();
            }
            return writer;
          },

          decode(): CustomMsgDelegate {
            throw new Error("decode method should not be required");
          },

          fromJSON(): CustomMsgDelegate {
            throw new Error("fromJSON method should not be required");
          },

          fromPartial(object: DeepPartial<CustomMsgDelegate>): CustomMsgDelegate {
            const message = { ...baseCustomMsgDelegate } as CustomMsgDelegate;
            if (object.customDelegatorAddress !== undefined && object.customDelegatorAddress !== null) {
              message.customDelegatorAddress = object.customDelegatorAddress;
            } else {
              message.customDelegatorAddress = "";
            }
            if (object.customValidatorAddress !== undefined && object.customValidatorAddress !== null) {
              message.customValidatorAddress = object.customValidatorAddress;
            } else {
              message.customValidatorAddress = "";
            }
            if (object.customAmount !== undefined && object.customAmount !== null) {
              message.customAmount = Coin.fromPartial(object.customAmount);
            } else {
              message.customAmount = undefined;
            }
            return message;
          },

          toJSON(): unknown {
            throw new Error("toJSON method should not be required");
          },
        };
        customRegistry.register(msgDelegateTypeUrl, CustomMsgDelegate);
        const customAminoTypes = new AminoTypes({
          additions: {
            "/cosmos.staking.v1beta1.MsgDelegate": {
              aminoType: "cosmos-sdk/MsgDelegate",
              toAmino: ({
                customDelegatorAddress,
                customValidatorAddress,
                customAmount,
              }: CustomMsgDelegate): AminoMsgDelegate["value"] => {
                assert(customDelegatorAddress, "missing customDelegatorAddress");
                assert(customValidatorAddress, "missing validatorAddress");
                assert(customAmount, "missing amount");
                return {
                  delegator_address: customDelegatorAddress,
                  validator_address: customValidatorAddress,
                  amount: {
                    amount: customAmount.amount,
                    denom: customAmount.denom,
                  },
                };
              },
              fromAmino: ({
                delegator_address,
                validator_address,
                amount,
              }: AminoMsgDelegate["value"]): CustomMsgDelegate => ({
                customDelegatorAddress: delegator_address,
                customValidatorAddress: validator_address,
                customAmount: Coin.fromPartial(amount),
              }),
            },
          },
        });
        const options = { registry: customRegistry, aminoTypes: customAminoTypes };
        const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet, options);

        const msg: CustomMsgDelegate = {
          customDelegatorAddress: faucet.address0,
          customValidatorAddress: validator.validatorAddress,
          customAmount: coin(1234, "ustake"),
        };
        const msgAny = {
          typeUrl: "/cosmos.staking.v1beta1.MsgDelegate",
          value: msg,
        };
        const fee = {
          amount: coins(2000, "ucosm"),
          gas: "200000",
        };
        const memo = "Use your power wisely";
        const result = await client.signAndBroadcast(faucet.address0, [msgAny], fee, memo);
        assertIsBroadcastTxSuccess(result);
      }


({
                customDelegatorAddress,
                customValidatorAddress,
                customAmount,
              }: CustomMsgDelegate): AminoMsgDelegate["value"] => {
                assert(customDelegatorAddress, "missing customDelegatorAddress");
                assert(customValidatorAddress, "missing validatorAddress");
                assert(customAmount, "missing amount");
                return {
                  delegator_address: customDelegatorAddress,
                  validator_address: customValidatorAddress,
                  amount: {
                    amount: customAmount.amount,
                    denom: customAmount.denom,
                  },
                };
              }


({
                delegator_address,
                validator_address,
                amount,
              }: AminoMsgDelegate["value"]): CustomMsgDelegate => ({
                customDelegatorAddress: delegator_address,
                customValidatorAddress: validator_address,
                customAmount: Coin.fromPartial(amount),
              })


async () => {
        pendingWithoutSimapp();
        const wallet = await ModifyingSecp256k1HdWallet.fromMnemonic(faucet.mnemonic);
        const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet);

        const msg: MsgDelegate = {
          delegatorAddress: faucet.address0,
          validatorAddress: validator.validatorAddress,
          amount: coin(1234, "ustake"),
        };
        const msgAny: MsgDelegateEncodeObject = {
          typeUrl: "/cosmos.staking.v1beta1.MsgDelegate",
          value: msg,
        };
        const fee = {
          amount: coins(2000, "ucosm"),
          gas: "200000",
        };
        const memo = "Use your power wisely";
        const result = await client.signAndBroadcast(faucet.address0, [msgAny], fee, memo);
        assertIsBroadcastTxSuccess(result);

        await sleep(1000);

        const searchResult = await client.getTx(result.transactionHash);
        assert(searchResult, "Must find transaction");
        const tx = decodeTxRaw(searchResult.tx);
        // From ModifyingSecp256k1HdWallet
        expect(tx.body.memo).toEqual("This was modified");
        expect({ ...tx.authInfo.fee!.amount[0] }).toEqual(coin(3000, "ucosm"));
        expect(tx.authInfo.fee!.gasLimit.toNumber()).toEqual(333333);
      }


() => {
      it("works", async () => {
        pendingWithoutSimapp();
        const wallet = await DirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic);
        const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet);

        const msg = MsgDelegate.fromPartial({
          delegatorAddress: faucet.address0,
          validatorAddress: validator.validatorAddress,
          amount: coin(1234, "ustake"),
        });
        const msgAny: MsgDelegateEncodeObject = {
          typeUrl: "/cosmos.staking.v1beta1.MsgDelegate",
          value: msg,
        };
        const fee = {
          amount: coins(2000, "ucosm"),
          gas: "180000", // 180k
        };
        const memo = "Use your power wisely";
        const signed = await client.sign(faucet.address0, [msgAny], fee, memo);

        // ensure signature is valid
        const result = await client.broadcastTx(Uint8Array.from(TxRaw.encode(signed).finish()));
        assertIsBroadcastTxSuccess(result);
      });

      it("works with a modifying signer", async () => {
        pendingWithoutSimapp();
        const wallet = await ModifyingDirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic);
        const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet);

        const msg = MsgDelegate.fromPartial({
          delegatorAddress: faucet.address0,
          validatorAddress: validator.validatorAddress,
          amount: coin(1234, "ustake"),
        });
        const msgAny: MsgDelegateEncodeObject = {
          typeUrl: "/cosmos.staking.v1beta1.MsgDelegate",
          value: msg,
        };
        const fee = {
          amount: coins(2000, "ucosm"),
          gas: "180000", // 180k
        };
        const memo = "Use your power wisely";
        const signed = await client.sign(faucet.address0, [msgAny], fee, memo);

        const body = TxBody.decode(signed.bodyBytes);
        const authInfo = AuthInfo.decode(signed.authInfoBytes);
        // From ModifyingDirectSecp256k1HdWallet
        expect(body.memo).toEqual("This was modified");
        expect({ ...authInfo.fee!.amount[0] }).toEqual(coin(3000, "ucosm"));
        expect(authInfo.fee!.gasLimit.toNumber()).toEqual(333333);

        // ensure signature is valid
        const result = await client.broadcastTx(Uint8Array.from(TxRaw.encode(signed).finish()));
        assertIsBroadcastTxSuccess(result);
      });
    }


async () => {
        pendingWithoutSimapp();
        const wallet = await DirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic);
        const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet);

        const msg = MsgDelegate.fromPartial({
          delegatorAddress: faucet.address0,
          validatorAddress: validator.validatorAddress,
          amount: coin(1234, "ustake"),
        });
        const msgAny: MsgDelegateEncodeObject = {
          typeUrl: "/cosmos.staking.v1beta1.MsgDelegate",
          value: msg,
        };
        const fee = {
          amount: coins(2000, "ucosm"),
          gas: "180000", // 180k
        };
        const memo = "Use your power wisely";
        const signed = await client.sign(faucet.address0, [msgAny], fee, memo);

        // ensure signature is valid
        const result = await client.broadcastTx(Uint8Array.from(TxRaw.encode(signed).finish()));
        assertIsBroadcastTxSuccess(result);
      }


async () => {
        pendingWithoutSimapp();
        const wallet = await ModifyingDirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic);
        const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet);

        const msg = MsgDelegate.fromPartial({
          delegatorAddress: faucet.address0,
          validatorAddress: validator.validatorAddress,
          amount: coin(1234, "ustake"),
        });
        const msgAny: MsgDelegateEncodeObject = {
          typeUrl: "/cosmos.staking.v1beta1.MsgDelegate",
          value: msg,
        };
        const fee = {
          amount: coins(2000, "ucosm"),
          gas: "180000", // 180k
        };
        const memo = "Use your power wisely";
        const signed = await client.sign(faucet.address0, [msgAny], fee, memo);

        const body = TxBody.decode(signed.bodyBytes);
        const authInfo = AuthInfo.decode(signed.authInfoBytes);
        // From ModifyingDirectSecp256k1HdWallet
        expect(body.memo).toEqual("This was modified");
        expect({ ...authInfo.fee!.amount[0] }).toEqual(coin(3000, "ucosm"));
        expect(authInfo.fee!.gasLimit.toNumber()).toEqual(333333);

        // ensure signature is valid
        const result = await client.broadcastTx(Uint8Array.from(TxRaw.encode(signed).finish()));
        assertIsBroadcastTxSuccess(result);
      }


async () => {
        pendingWithoutSimapp();
        const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);
        const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet);

        const msgSend: MsgSend = {
          fromAddress: faucet.address0,
          toAddress: makeRandomAddress(),
          amount: coins(1234, "ucosm"),
        };
        const msgAny: MsgSendEncodeObject = {
          typeUrl: "/cosmos.bank.v1beta1.MsgSend",
          value: msgSend,
        };
        const fee = {
          amount: coins(2000, "ucosm"),
          gas: "200000",
        };
        const memo = "Use your tokens wisely";
        const signed = await client.sign(faucet.address0, [msgAny], fee, memo);

        // ensure signature is valid
        const result = await client.broadcastTx(Uint8Array.from(TxRaw.encode(signed).finish()));
        assertIsBroadcastTxSuccess(result);
      }


async () => {
        pendingWithoutSimapp();
        const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);
        const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet);

        const msgDelegate: MsgDelegate = {
          delegatorAddress: faucet.address0,
          validatorAddress: validator.validatorAddress,
          amount: coin(1234, "ustake"),
        };
        const msgAny: MsgDelegateEncodeObject = {
          typeUrl: "/cosmos.staking.v1beta1.MsgDelegate",
          value: msgDelegate,
        };
        const fee = {
          amount: coins(2000, "ustake"),
          gas: "200000",
        };
        const memo = "Use your tokens wisely";
        const signed = await client.sign(faucet.address0, [msgAny], fee, memo);

        // ensure signature is valid
        const result = await client.broadcastTx(Uint8Array.from(TxRaw.encode(signed).finish()));
        assertIsBroadcastTxSuccess(result);
      }


async () => {
        pendingWithoutSimapp();
        const wallet = await Secp256k1HdWallet.fromMnemonic(faucet.mnemonic);

        const customRegistry = new Registry();
        const msgDelegateTypeUrl = "/cosmos.staking.v1beta1.MsgDelegate";
        interface CustomMsgDelegate {
          customDelegatorAddress?: string;
          customValidatorAddress?: string;
          customAmount?: Coin;
        }
        const baseCustomMsgDelegate: CustomMsgDelegate = {
          customDelegatorAddress: "",
          customValidatorAddress: "",
        };
        const CustomMsgDelegate = {
          // Adapted from autogenerated MsgDelegate implementation
          encode(
            message: CustomMsgDelegate,
            writer: protobuf.Writer = protobuf.Writer.create(),
          ): protobuf.Writer {
            writer.uint32(10).string(message.customDelegatorAddress ?? "");
            writer.uint32(18).string(message.customValidatorAddress ?? "");
            if (message.customAmount !== undefined && message.customAmount !== undefined) {
              Coin.encode(message.customAmount, writer.uint32(26).fork()).ldelim();
            }
            return writer;
          },

          decode(): CustomMsgDelegate {
            throw new Error("decode method should not be required");
          },

          fromJSON(): CustomMsgDelegate {
            throw new Error("fromJSON method should not be required");
          },

          fromPartial(object: DeepPartial<CustomMsgDelegate>): CustomMsgDelegate {
            const message = { ...baseCustomMsgDelegate } as CustomMsgDelegate;
            if (object.customDelegatorAddress !== undefined && object.customDelegatorAddress !== null) {
              message.customDelegatorAddress = object.customDelegatorAddress;
            } else {
              message.customDelegatorAddress = "";
            }
            if (object.customValidatorAddress !== undefined && object.customValidatorAddress !== null) {
              message.customValidatorAddress = object.customValidatorAddress;
            } else {
              message.customValidatorAddress = "";
            }
            if (object.customAmount !== undefined && object.customAmount !== null) {
              message.customAmount = Coin.fromPartial(object.customAmount);
            } else {
              message.customAmount = undefined;
            }
            return message;
          },

          toJSON(): unknown {
            throw new Error("toJSON method should not be required");
          },
        };
        customRegistry.register(msgDelegateTypeUrl, CustomMsgDelegate);
        const customAminoTypes = new AminoTypes({
          additions: {
            "/cosmos.staking.v1beta1.MsgDelegate": {
              aminoType: "cosmos-sdk/MsgDelegate",
              toAmino: ({
                customDelegatorAddress,
                customValidatorAddress,
                customAmount,
              }: CustomMsgDelegate): AminoMsgDelegate["value"] => {
                assert(customDelegatorAddress, "missing customDelegatorAddress");
                assert(customValidatorAddress, "missing validatorAddress");
                assert(customAmount, "missing amount");
                return {
                  delegator_address: customDelegatorAddress,
                  validator_address: customValidatorAddress,
                  amount: {
                    amount: customAmount.amount,
                    denom: customAmount.denom,
                  },
                };
              },
              fromAmino: ({
                delegator_address,
                validator_address,
                amount,
              }: AminoMsgDelegate["value"]): CustomMsgDelegate => ({
                customDelegatorAddress: delegator_address,
                customValidatorAddress: validator_address,
                customAmount: Coin.fromPartial(amount),
              }),
            },
          },
        });
        const options = { registry: customRegistry, aminoTypes: customAminoTypes };
        const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet, options);

        const msg: CustomMsgDelegate = {
          customDelegatorAddress: faucet.address0,
          customValidatorAddress: validator.validatorAddress,
          customAmount: coin(1234, "ustake"),
        };
        const msgAny = {
          typeUrl: "/cosmos.staking.v1beta1.MsgDelegate",
          value: msg,
        };
        const fee = {
          amount: coins(2000, "ucosm"),
          gas: "200000",
        };
        const memo = "Use your power wisely";
        const signed = await client.sign(faucet.address0, [msgAny], fee, memo);

        // ensure signature is valid
        const result = await client.broadcastTx(Uint8Array.from(TxRaw.encode(signed).finish()));
        assertIsBroadcastTxSuccess(result);
      }


async () => {
        pendingWithoutSimapp();
        const wallet = await ModifyingSecp256k1HdWallet.fromMnemonic(faucet.mnemonic);
        const client = await SigningStargateClient.connectWithSigner(simapp.tendermintUrl, wallet);

        const msg: MsgDelegate = {
          delegatorAddress: faucet.address0,
          validatorAddress: validator.validatorAddress,
          amount: coin(1234, "ustake"),
        };
        const msgAny: MsgDelegateEncodeObject = {
          typeUrl: "/cosmos.staking.v1beta1.MsgDelegate",
          value: msg,
        };
        const fee = {
          amount: coins(2000, "ucosm"),
          gas: "200000",
        };
        const memo = "Use your power wisely";
        const signed = await client.sign(faucet.address0, [msgAny], fee, memo);

        const body = TxBody.decode(signed.bodyBytes);
        const authInfo = AuthInfo.decode(signed.authInfoBytes);
        // From ModifyingSecp256k1HdWallet
        expect(body.memo).toEqual("This was modified");
        expect({ ...authInfo.fee!.amount[0] }).toEqual(coin(3000, "ucosm"));
        expect(authInfo.fee!.gasLimit.toNumber()).toEqual(333333);

        // ensure signature is valid
        const result = await client.broadcastTx(Uint8Array.from(TxRaw.encode(signed).finish()));
        assertIsBroadcastTxSuccess(result);
      }


interface CustomMsgDelegate {
          customDelegatorAddress?: string;
          customValidatorAddress?: string;
          customAmount?: Coin;
        }


// Adapted from autogenerated MsgDelegate implementation
encode(
            message: CustomMsgDelegate,
            writer: protobuf.Writer = protobuf.Writer.create(),
          ): protobuf.Writer {
            writer.uint32(10).string(message.customDelegatorAddress ?? "");
            writer.uint32(18).string(message.customValidatorAddress ?? "");
            if (message.customAmount !== undefined && message.customAmount !== undefined) {
              Coin.encode(message.customAmount, writer.uint32(26).fork()).ldelim();
            }
            return writer;
          }


decode(): CustomMsgDelegate {
            throw new Error("decode method should not be required");
          }


fromJSON(): CustomMsgDelegate {
            throw new Error("fromJSON method should not be required");
          }


fromPartial(object: DeepPartial<CustomMsgDelegate>): CustomMsgDelegate {
            const message = { ...baseCustomMsgDelegate } as CustomMsgDelegate;
            if (object.customDelegatorAddress !== undefined && object.customDelegatorAddress !== null) {
              message.customDelegatorAddress = object.customDelegatorAddress;
            } else {
              message.customDelegatorAddress = "";
            }
            if (object.customValidatorAddress !== undefined && object.customValidatorAddress !== null) {
              message.customValidatorAddress = object.customValidatorAddress;
            } else {
              message.customValidatorAddress = "";
            }
            if (object.customAmount !== undefined && object.customAmount !== null) {
              message.customAmount = Coin.fromPartial(object.customAmount);
            } else {
              message.customAmount = undefined;
            }
            return message;
          }


toJSON(): unknown {
            throw new Error("toJSON method should not be required");
          }


class Rental {
    public movie: Movie;
    public days: number;

    constructor(movie: Movie, days: number) {
        this.movie = movie;
        this.days = days;
    }

    public getRentalPrice(): Money {
        const moviePrice = this.movie.price;
        return this.computeRentalPrice(
            moviePrice.basePrice,
            moviePrice.pricePerExtraDay,
            this.movie.rentalDays,
            this.days);
    }

    public getFrequentRenterPoints(): number {
        if (this.days < this.movie.frequentRenterMinimumDays) {
            return this.movie.frequentRenterBasePoints;
        } else {
            return this.movie.frequentRenterPointsForExtraDays;
        }
    }

    public computeRentalPrice(basePrice: Money,
                              pricePerExtraDay: Money,
                              minimumRentalDays: number,
                              daysRented: number): Money {
        if (daysRented <= 0) {
            throw new Error('daysRented must be higher than zero');
        }

        let price = basePrice;

        daysRented = Math.ceil(daysRented);

        if (daysRented > minimumRentalDays) {
            price = price.add(pricePerExtraDay.times(daysRented - minimumRentalDays));
        }

        return price;
    }
}


public getRentalPrice(): Money {
        const moviePrice = this.movie.price;
        return this.computeRentalPrice(
            moviePrice.basePrice,
            moviePrice.pricePerExtraDay,
            this.movie.rentalDays,
            this.days);
    }


public getFrequentRenterPoints(): number {
        if (this.days < this.movie.frequentRenterMinimumDays) {
            return this.movie.frequentRenterBasePoints;
        } else {
            return this.movie.frequentRenterPointsForExtraDays;
        }
    }


public computeRentalPrice(basePrice: Money,
                              pricePerExtraDay: Money,
                              minimumRentalDays: number,
                              daysRented: number): Money {
        if (daysRented <= 0) {
            throw new Error('daysRented must be higher than zero');
        }

        let price = basePrice;

        daysRented = Math.ceil(daysRented);

        if (daysRented > minimumRentalDays) {
            price = price.add(pricePerExtraDay.times(daysRented - minimumRentalDays));
        }

        return price;
    }


@Injectable()
export class DeleteAccountByIdService
{
    constructor(
        private readonly publisher: EventPublisher,
        private readonly repository: IAccountRepository,
    ) {}

    async main(
        id: AccountId,
        constraint?: QueryStatement,
        cQMetadata?: CQMetadata,
    ): Promise<void>
    {
        // get object to delete
        const account = await this.repository.findById(id, { constraint, cQMetadata });

        // it is not necessary to pass the constraint in the delete, if the object
        // is not found in the findById, an exception will be thrown.
        await this.repository.deleteById(
            account.id,
            {
                deleteOptions: cQMetadata?.repositoryOptions,
                cQMetadata,
            },
        );

        // insert EventBus in object, to be able to apply and commit events
        const accountRegister = this.publisher.mergeObjectContext(account);

        accountRegister.deleted(account); // apply event to model events
        accountRegister.commit(); // commit all events of model
    }
}


async main(
        id: AccountId,
        constraint?: QueryStatement,
        cQMetadata?: CQMetadata,
    ): Promise<void>
    {
        // get object to delete
        const account = await this.repository.findById(id, { constraint, cQMetadata });

        // it is not necessary to pass the constraint in the delete, if the object
        // is not found in the findById, an exception will be thrown.
        await this.repository.deleteById(
            account.id,
            {
                deleteOptions: cQMetadata?.repositoryOptions,
                cQMetadata,
            },
        );

        // insert EventBus in object, to be able to apply and commit events
        const accountRegister = this.publisher.mergeObjectContext(account);

        accountRegister.deleted(account); // apply event to model events
        accountRegister.commit(); // commit all events of model
    }


() => {

    it('is rule', () => {
        assert.instanceOf(new Factor(0), AbstractRule);
    });

    it('values is valid', () => {
        assert.isTrue(new Factor(1).validate('1'));
        assert.isTrue(new Factor(6).validate('1'));
        assert.isTrue(new Factor(1).validate(1));
        assert.isTrue(new Factor(2).validate(1));
        assert.isTrue(new Factor(2).validate(2));
        assert.isTrue(new Factor(3).validate(1));
        assert.isTrue(new Factor(3).validate(3));
        assert.isTrue(new Factor(4).validate(1));
        assert.isTrue(new Factor(4).validate(2));
        assert.isTrue(new Factor(4).validate(4));
        assert.isTrue(new Factor(5).validate(1));
        assert.isTrue(new Factor(5).validate(5));
        assert.isTrue(new Factor(6).validate(1));
        assert.isTrue(new Factor(6).validate(2));
        assert.isTrue(new Factor(6).validate(3));
        assert.isTrue(new Factor(6).validate(6));
        assert.isTrue(new Factor(0).validate(0));
        assert.isTrue(new Factor(0).validate(1));
    });

    it('values is not valid', () => {
        assert.isFalse(new Factor(1).validate(1.1));
        assert.isFalse(new Factor(1).validate(-1.1));
        assert.isFalse(new Factor(1).validate('0.2'));
        assert.isFalse(new Factor(1).validate('.2'));
        assert.isFalse(new Factor(1).validate('-.2'));
        assert.isFalse(new Factor(1).validate('165.7'));
        assert.isFalse(new Factor(1).validate(''));
        assert.isFalse(new Factor(1).validate(null));
        assert.isFalse(new Factor(1).validate('a'));
        assert.isFalse(new Factor(1).validate(' '));
        assert.isFalse(new Factor(1).validate('Foo'));
        assert.isFalse(new Factor(3).validate(2));
        assert.isFalse(new Factor(4).validate(3));
        assert.isFalse(new Factor(5).validate(2));
        assert.isFalse(new Factor(5).validate(3));
        assert.isFalse(new Factor(5).validate(4));
        assert.isFalse(new Factor(1).validate(0));
        assert.isFalse(new Factor(2).validate(0));
        assert.isFalse(new Factor(0.5).validate(null));
        assert.isFalse(new Factor(1.5).validate(null));
        assert.isFalse(new Factor(-0.5).validate(null));
        assert.isFalse(new Factor(-1.5).validate(null));
        assert.isFalse(new Factor(-1.5).validate(undefined));
        assert.isFalse(new Factor(-1.5).validate(false));
        assert.isFalse(new Factor(Number.MAX_SAFE_INTEGER + 1).validate(null));
    });

}


() => {
        assert.instanceOf(new Factor(0), AbstractRule);
    }


() => {
        assert.isTrue(new Factor(1).validate('1'));
        assert.isTrue(new Factor(6).validate('1'));
        assert.isTrue(new Factor(1).validate(1));
        assert.isTrue(new Factor(2).validate(1));
        assert.isTrue(new Factor(2).validate(2));
        assert.isTrue(new Factor(3).validate(1));
        assert.isTrue(new Factor(3).validate(3));
        assert.isTrue(new Factor(4).validate(1));
        assert.isTrue(new Factor(4).validate(2));
        assert.isTrue(new Factor(4).validate(4));
        assert.isTrue(new Factor(5).validate(1));
        assert.isTrue(new Factor(5).validate(5));
        assert.isTrue(new Factor(6).validate(1));
        assert.isTrue(new Factor(6).validate(2));
        assert.isTrue(new Factor(6).validate(3));
        assert.isTrue(new Factor(6).validate(6));
        assert.isTrue(new Factor(0).validate(0));
        assert.isTrue(new Factor(0).validate(1));
    }


() => {
        assert.isFalse(new Factor(1).validate(1.1));
        assert.isFalse(new Factor(1).validate(-1.1));
        assert.isFalse(new Factor(1).validate('0.2'));
        assert.isFalse(new Factor(1).validate('.2'));
        assert.isFalse(new Factor(1).validate('-.2'));
        assert.isFalse(new Factor(1).validate('165.7'));
        assert.isFalse(new Factor(1).validate(''));
        assert.isFalse(new Factor(1).validate(null));
        assert.isFalse(new Factor(1).validate('a'));
        assert.isFalse(new Factor(1).validate(' '));
        assert.isFalse(new Factor(1).validate('Foo'));
        assert.isFalse(new Factor(3).validate(2));
        assert.isFalse(new Factor(4).validate(3));
        assert.isFalse(new Factor(5).validate(2));
        assert.isFalse(new Factor(5).validate(3));
        assert.isFalse(new Factor(5).validate(4));
        assert.isFalse(new Factor(1).validate(0));
        assert.isFalse(new Factor(2).validate(0));
        assert.isFalse(new Factor(0.5).validate(null));
        assert.isFalse(new Factor(1.5).validate(null));
        assert.isFalse(new Factor(-0.5).validate(null));
        assert.isFalse(new Factor(-1.5).validate(null));
        assert.isFalse(new Factor(-1.5).validate(undefined));
        assert.isFalse(new Factor(-1.5).validate(false));
        assert.isFalse(new Factor(Number.MAX_SAFE_INTEGER + 1).validate(null));
    }


export function setup(router: CuriRouter) {
  const _initial = router.current();
  _navAndresp.navigation = _initial.navigation;
  _navAndresp.response = _initial.response;

  return router.observe(({ response, navigation }) => {
    _navAndresp.navigation = navigation;
    _navAndresp.response = response;
  });
}


({ response, navigation }) => {
    _navAndresp.navigation = navigation;
    _navAndresp.response = response;
  }


export default class UsersController {
  public async create(request: Request, response: Response): Promise<Response> {
    try {
      const { name, email, password } = request.body;

      const createUser = container.resolve(CreateUserService);

      const user = await createUser.execute({ name, email, password });

      return response.json({ user: classToClass(user) });
    } catch (error) {
      return response.status(400).json({ message: error.message });
    }
  }
}


public async create(request: Request, response: Response): Promise<Response> {
    try {
      const { name, email, password } = request.body;

      const createUser = container.resolve(CreateUserService);

      const user = await createUser.execute({ name, email, password });

      return response.json({ user: classToClass(user) });
    } catch (error) {
      return response.status(400).json({ message: error.message });
    }
  }


@Component({
  tag: 'svg-stepper',
  styleUrl: 'svg-stepper.css',
})
export class SvgStepper {
  render() {
    return (

Hello SvgStepper!

    );
  }
}


render() {
    return (

Hello SvgStepper!

    );
  }


interface HTMLElementTagNameMap {
        'sp-icons-editor': IconsEditor;
    }


function computeCustomTemplatesPath(configPath: string | undefined, customTemplatesPath: string) {
	if (configPath) {
		return path.resolve(path.dirname(configPath), customTemplatesPath)
	} else {
		return customTemplatesPath
	}
}


(config, context) => {
	const javaLikeContext: JavaLikeContext = {
		...context,
		defaultConstantStyle: ConstantStyle.allCapsSnake,
	}

	const generatorOptions: CodegenOptionsDocumentation = {
		...javaLikeOptions(config, javaLikeContext),
		customTemplatesPath: config.customTemplates && computeCustomTemplatesPath(config.configPath, config.customTemplates),
	}

	const aCommonGenerator = commonGenerator(config, context)

	return {
		...context.baseGenerator(config, context),
		...aCommonGenerator,
		...javaLikeGenerator(config, javaLikeContext),
		generatorType: () => CodegenGeneratorType.DOCUMENTATION,
		toLiteral: (value, options) => {
			if (value === undefined) {
				return context.generator().defaultValue(options).literalValue
			}

			return `${value}`
		},
		toNativeType: (options) => {
			const { type, format } = options
			if (type === 'string') {
				if (format) {
					return new context.NativeType(format, {
						serializedType: 'string',
					})
				}
			} else if (type === 'integer') {
				if (format) {
					return new context.NativeType(format, {
						serializedType: 'number',
					})
				}
			}

			return new context.NativeType(type, {
				serializedType: null,
			})
		},
		toNativeObjectType: function(options) {
			const { scopedName } = options
			let modelName = ''
			for (const name of scopedName) {
				modelName += `.${context.generator().toClassName(name)}`
			}
			return new context.NativeType(modelName.substring(1))
		},
		toNativeArrayType: (options) => {
			const { componentNativeType } = options
			return new context.NativeType(`${componentNativeType}[]`)
		},
		toNativeMapType: (options) => {
			const { keyNativeType, componentNativeType } = options
			return new context.NativeType(`{ [name: ${keyNativeType}]: ${componentNativeType} }`)
		},
		nativeTypeUsageTransformer: ({ nullable }) => ({
			default: function(nativeType, nativeTypeString) {
				if (nullable) {
					return `${nativeTypeString} | null`
				}
				return nativeTypeString
			},
			/* We don't transform the concrete type as the concrete type is never null; we use it to make new objects */
			concreteType: null,
		}),
		defaultValue: () => {
			return { value: null, literalValue: 'undefined' }
		},
		initialValue: () => {
			return { value: null, literalValue: 'undefined' }
		},
		toOperationGroupName: (name) => {
			return name
		},
		operationGroupingStrategy: () => {
			return context.operationGroupingStrategies.addToGroupsByTagOrPath
		},

		watchPaths: () => {
			const result = [path.resolve(__dirname, '..', 'templates')]
			result.push(path.resolve(__dirname, '..', 'less'))
			result.push(path.resolve(__dirname, '..', 'static'))
			if (config.customTemplates) {
				result.push(computeCustomTemplatesPath(config.configPath, config.customTemplates))
			}
			return result
		},

		cleanPathPatterns: () => undefined,

		templateRootContext: () => {
			return {
				...aCommonGenerator.templateRootContext(),
				...generatorOptions,
				generatorClass: '@openapi-generator-plus/plain-documentation-generator',
			}
		},

		exportTemplates: async(outputPath, doc) => {
			const hbs = Handlebars.create()

			registerStandardHelpers(hbs, context)
			// eslint-disable-next-line @typescript-eslint/no-explicit-any
			hbs.registerHelper('eachSorted', function(this: unknown, context: unknown, options: Handlebars.HelperOptions) {
				if (!context) {
					return options.inverse(this)
				}

				// eslint-disable-next-line @typescript-eslint/no-explicit-any
				let collection: any[]
				if (context instanceof Map) {
					collection = [...context.values()]
				} else if (Array.isArray(context)) {
					collection = context
				} else if (typeof context === 'object') {
					collection = []
					for (const key in context) {
						// eslint-disable-next-line @typescript-eslint/no-explicit-any
						collection.push((context as any)[key])
					}
				} else {
					collection = [context]
				}

				let result = ''
				// eslint-disable-next-line @typescript-eslint/no-explicit-any
				for (const item of collection.sort(function(a: any, b: any) {
					if (a === b) {
						return 0
					}
					if (typeof a === 'string' && typeof b === 'string') {
						return a.localeCompare(b)
					} else if (typeof a === 'number' && typeof b === 'number') {
						return a < b ? -1 : a > b ? 1 : 0
					} else if (typeof a === 'object' && typeof b === 'object') {
						if (a === null) {
							return 1
						} else if (b === null) {
							return -1
						}

						if (a.httpMethod && b.httpMethod) {
							/* Sort CodegenOperations by http method and then by name */
							const result = compareHttpMethods(a.httpMethod, b.httpMethod)
							if (result !== 0) {
								return result
							}
						}

						if (a.name && b.name) {
							return a.name.localeCompare(b.name)
						}

						return 0
					} else {
						return 0
					}
				})) {
					result += options.fn(item)
				}
				return result
			})
			hbs.registerHelper('htmlId', function(value: string) {
				if (value !== undefined) {
					return `${value}`.replace(/[^-a-zA-Z0-9_]+/g, '_').replace(/^_+/, '').replace(/_+$/, '')
				} else {
					return value
				}
			})

			await loadTemplates(path.resolve(__dirname, '..', 'templates'), hbs)

			if (generatorOptions.customTemplatesPath) {
				await loadTemplates(generatorOptions.customTemplatesPath, hbs)
			}

			const rootContext = context.generator().templateRootContext()

			if (!outputPath.endsWith('/')) {
				outputPath += '/'
			}

			await emit('index', path.join(outputPath, 'index.html'), { ...rootContext, ...doc }, true, hbs)

			emitLess('style.less', path.join(outputPath, 'main.css'))
			copyContents(path.resolve(__dirname, '..', 'static'), outputPath)
		},
	}
}


() => CodegenGeneratorType.DOCUMENTATION


(value, options) => {
			if (value === undefined) {
				return context.generator().defaultValue(options).literalValue
			}

			return `${value}`
		}


(options) => {
			const { type, format } = options
			if (type === 'string') {
				if (format) {
					return new context.NativeType(format, {
						serializedType: 'string',
					})
				}
			} else if (type === 'integer') {
				if (format) {
					return new context.NativeType(format, {
						serializedType: 'number',
					})
				}
			}

			return new context.NativeType(type, {
				serializedType: null,
			})
		}


(options) => {
			const { componentNativeType } = options
			return new context.NativeType(`${componentNativeType}[]`)
		}


(options) => {
			const { keyNativeType, componentNativeType } = options
			return new context.NativeType(`{ [name: ${keyNativeType}]: ${componentNativeType} }`)
		}


({ nullable }) => ({
			default: function(nativeType, nativeTypeString) {
				if (nullable) {
					return `${nativeTypeString} | null`
				}
				return nativeTypeString
			},
			/* We don't transform the concrete type as the concrete type is never null; we use it to make new objects */
			concreteType: null,
		})


() => {
			return { value: null, literalValue: 'undefined' }
		}


(name) => {
			return name
		}


() => {
			return context.operationGroupingStrategies.addToGroupsByTagOrPath
		}


() => {
			const result = [path.resolve(__dirname, '..', 'templates')]
			result.push(path.resolve(__dirname, '..', 'less'))
			result.push(path.resolve(__dirname, '..', 'static'))
			if (config.customTemplates) {
				result.push(computeCustomTemplatesPath(config.configPath, config.customTemplates))
			}
			return result
		}


() => {
			return {
				...aCommonGenerator.templateRootContext(),
				...generatorOptions,
				generatorClass: '@openapi-generator-plus/plain-documentation-generator',
			}
		}


async(outputPath, doc) => {
			const hbs = Handlebars.create()

			registerStandardHelpers(hbs, context)
			// eslint-disable-next-line @typescript-eslint/no-explicit-any
			hbs.registerHelper('eachSorted', function(this: unknown, context: unknown, options: Handlebars.HelperOptions) {
				if (!context) {
					return options.inverse(this)
				}

				// eslint-disable-next-line @typescript-eslint/no-explicit-any
				let collection: any[]
				if (context instanceof Map) {
					collection = [...context.values()]
				} else if (Array.isArray(context)) {
					collection = context
				} else if (typeof context === 'object') {
					collection = []
					for (const key in context) {
						// eslint-disable-next-line @typescript-eslint/no-explicit-any
						collection.push((context as any)[key])
					}
				} else {
					collection = [context]
				}

				let result = ''
				// eslint-disable-next-line @typescript-eslint/no-explicit-any
				for (const item of collection.sort(function(a: any, b: any) {
					if (a === b) {
						return 0
					}
					if (typeof a === 'string' && typeof b === 'string') {
						return a.localeCompare(b)
					} else if (typeof a === 'number' && typeof b === 'number') {
						return a < b ? -1 : a > b ? 1 : 0
					} else if (typeof a === 'object' && typeof b === 'object') {
						if (a === null) {
							return 1
						} else if (b === null) {
							return -1
						}

						if (a.httpMethod && b.httpMethod) {
							/* Sort CodegenOperations by http method and then by name */
							const result = compareHttpMethods(a.httpMethod, b.httpMethod)
							if (result !== 0) {
								return result
							}
						}

						if (a.name && b.name) {
							return a.name.localeCompare(b.name)
						}

						return 0
					} else {
						return 0
					}
				})) {
					result += options.fn(item)
				}
				return result
			})
			hbs.registerHelper('htmlId', function(value: string) {
				if (value !== undefined) {
					return `${value}`.replace(/[^-a-zA-Z0-9_]+/g, '_').replace(/^_+/, '').replace(/_+$/, '')
				} else {
					return value
				}
			})

			await loadTemplates(path.resolve(__dirname, '..', 'templates'), hbs)

			if (generatorOptions.customTemplatesPath) {
				await loadTemplates(generatorOptions.customTemplatesPath, hbs)
			}

			const rootContext = context.generator().templateRootContext()

			if (!outputPath.endsWith('/')) {
				outputPath += '/'
			}

			await emit('index', path.join(outputPath, 'index.html'), { ...rootContext, ...doc }, true, hbs)

			emitLess('style.less', path.join(outputPath, 'main.css'))
			copyContents(path.resolve(__dirname, '..', 'static'), outputPath)
		}


() => new SharePlugin()


({ cadPackage }: { cadPackage: string }) => {
  return <DevIdePage cadPackage={cadPackage} />
}


():void => {
  i18next.addResourceBundle('PT', 'StaticPeoplesDevelopment', LanguagePT);
  i18next.addResourceBundle('PT', 'StaticIntroduction', LanguagePtFs);
  i18next.addResourceBundle('ENG', 'StaticPeoplesDevelopment', LanguageENG);
  i18next.addResourceBundle('ENG', 'StaticIntroduction', LanguageEngFs);

  useEffect((): void => {
    if (!i18next.hasResourceBundle('PT', 'StaticPeoplesDevelopment')) {
      i18next.addResourceBundle('PT', 'StaticPeoplesDevelopment', LanguagePT);
      i18next.addResourceBundle('PT', 'StaticIntroduction', LanguagePtFs);
    }
    if (!i18next.hasResourceBundle('ENG', 'StaticPeoplesDevelopment')) {
      i18next.addResourceBundle('ENG', 'StaticPeoplesDevelopment', LanguageENG);
      i18next.addResourceBundle('ENG', 'StaticIntroduction', LanguageEngFs);
    }
    // return type void != (): void... so as unknown as void
    return ((): void => {
      i18next.removeResourceBundle('PT', 'StaticPeoplesDevelopment');
      i18next.removeResourceBundle('PT', 'StaticIntroduction');
      i18next.removeResourceBundle('ENG', 'StaticPeoplesDevelopment');
      i18next.removeResourceBundle('ENG', 'StaticIntroduction');
    }) as unknown as void;
  }, []);
}


(): void => {
    if (!i18next.hasResourceBundle('PT', 'StaticPeoplesDevelopment')) {
      i18next.addResourceBundle('PT', 'StaticPeoplesDevelopment', LanguagePT);
      i18next.addResourceBundle('PT', 'StaticIntroduction', LanguagePtFs);
    }
    if (!i18next.hasResourceBundle('ENG', 'StaticPeoplesDevelopment')) {
      i18next.addResourceBundle('ENG', 'StaticPeoplesDevelopment', LanguageENG);
      i18next.addResourceBundle('ENG', 'StaticIntroduction', LanguageEngFs);
    }
    // return type void != (): void... so as unknown as void
    return ((): void => {
      i18next.removeResourceBundle('PT', 'StaticPeoplesDevelopment');
      i18next.removeResourceBundle('PT', 'StaticIntroduction');
      i18next.removeResourceBundle('ENG', 'StaticPeoplesDevelopment');
      i18next.removeResourceBundle('ENG', 'StaticIntroduction');
    }) as unknown as void;
  }


(): void => {
      i18next.removeResourceBundle('PT', 'StaticPeoplesDevelopment');
      i18next.removeResourceBundle('PT', 'StaticIntroduction');
      i18next.removeResourceBundle('ENG', 'StaticPeoplesDevelopment');
      i18next.removeResourceBundle('ENG', 'StaticIntroduction');
    }


() => {
  let modeHandler: ModeHandler;

  const { newTest, newTestOnly, newTestSkip } = getTestingFunctions();

  setup(async () => {
    await setupWorkspace();
    modeHandler = await getAndUpdateModeHandler();
  });

  teardown(cleanUpWorkspace);

  test('can be activated', async () => {
    modeHandler.vimState.editor = vscode.window.activeTextEditor!;

    await modeHandler.handleKeyEvent('<C-v>');
    assert.strictEqual(modeHandler.currentMode, Mode.VisualBlock);

    await modeHandler.handleKeyEvent('<C-v>');
    assert.strictEqual(modeHandler.currentMode, Mode.Normal);
  });

  newTest({
    title: 'Can handle A forward select',
    start: ['|test', 'test'],
    keysPressed: 'l<C-v>ljA123',
    end: ['tes123|t', 'tes123t'],
  });

  newTest({
    title: 'Can handle A backwards select',
    start: ['tes|t', 'test'],
    keysPressed: 'h<C-v>hjA123',
    end: ['tes123|t', 'tes123t'],
  });

  newTest({
    title: 'Can handle I forward select',
    start: ['|test', 'test'],
    keysPressed: 'l<C-v>ljI123',
    end: ['t123|est', 't123est'],
  });

  newTest({
    title: 'Can handle I backwards select',
    start: ['tes|t', 'test'],
    keysPressed: 'h<C-v>hjI123',
    end: ['t123|est', 't123est'],
  });

  newTest({
    title: 'Can handle I with empty lines on first character (inserts on empty line)',
    start: ['|test', '', 'test'],
    keysPressed: '<C-v>lljjI123',
    end: ['123|test', '123', '123test'],
  });

  newTest({
    title: 'Can handle I with empty lines on non-first character (does not insert on empty line)',
    start: ['t|est', '', 'test'],
    keysPressed: '<C-v>lljjI123',
    end: ['t123|est', '', 't123est'],
  });

  newTest({
    title: 'Can handle c forward select',
    start: ['|test', 'test'],
    keysPressed: 'l<C-v>ljc123',
    end: ['t123|t', 't123t'],
  });

  newTest({
    title: 'Can handle c backwards select',
    start: ['tes|t', 'test'],
    keysPressed: 'h<C-v>hjc123',
    end: ['t123|t', 't123t'],
  });

  newTest({
    title: 'Can handle C',
    start: ['tes|t', 'test'],
    keysPressed: 'h<C-v>hjC123',
    end: ['t123|', 't123'],
  });

  newTest({
    title: 'Can do a multi line replace',
    start: ['one |two three four five', 'one two three four five'],
    keysPressed: '<C-v>jeer1',
    end: ['one |111111111 four five', 'one 111111111 four five'],
    endMode: Mode.Normal,
  });

  newTest({
    title: "Can handle 'D'",
    start: ['tes|t', 'test'],
    keysPressed: '<C-v>hjD',
    end: ['t|e', 'te'],
  });

  newTest({
    title: "Can handle 'gj'",
    start: ['t|est', 'test'],
    keysPressed: '<C-v>gjI123',
    end: ['t123|est', 't123est'],
  });

  suite('Non-darwin <C-c> tests', () => {
    if (process.platform === 'darwin') {
      return;
    }

    test('<C-c> copies and sets mode to normal', async () => {
      await modeHandler.handleMultipleKeyEvents('ione two three'.split(''));
      await modeHandler.handleMultipleKeyEvents(['<Esc>', 'Y', 'p', 'p']);

      assertEqualLines(['one two three', 'one two three', 'one two three']);

      await modeHandler.handleMultipleKeyEvents(['<Esc>', 'H', '<C-v>', 'e', 'j', 'j', '<C-c>']);

      // ensuring we're back in normal
      assert.strictEqual(modeHandler.currentMode, Mode.Normal);

      // test copy by pasting back
      await modeHandler.handleMultipleKeyEvents(['H', '"', '+', 'P']);

      // TODO: should be
      // assertEqualLines(['oneone two three', 'oneone two three', 'oneone two three']);
      // unfortunately it is
      assertEqualLines(['one', 'one', 'one', 'one two three', 'one two three', 'one two three']);
    });
  });

  newTest({
    title: 'Properly add to end of lines j then $',
    start: ['|Dog', 'Angry', 'Dog', 'Angry', 'Dog'],
    keysPressed: '<C-v>4j$Aaa',
    end: ['Dogaa|', 'Angryaa', 'Dogaa', 'Angryaa', 'Dogaa'],
  });

  newTest({
    title: 'Properly add to end of lines $ then j',
    start: ['|Dog', 'Angry', 'Dog', 'Angry', 'Dog'],
    keysPressed: '<C-v>$4jAaa<Esc>',
    end: ['Doga|a', 'Angryaa', 'Dogaa', 'Angryaa', 'Dogaa'],
  });

  newTest({
    title: 'o works in visual block mode',
    start: ['|foo', 'bar', 'baz'],
    keysPressed: '<C-v>jjllold',
    end: ['|f', 'b', 'b'],
  });

  newTest({
    title: 'd deletes block',
    start: ['11111', '2|2222', '33333', '44444', '55555'],
    keysPressed: '<C-v>jjlld',
    end: ['11111', '2|2', '33', '44', '55555'],
  });

  newTest({
    title: 'x deletes block',
    start: ['11111', '2|2222', '33333', '44444', '55555'],
    keysPressed: '<C-v>jjllx',
    end: ['11111', '2|2', '33', '44', '55555'],
  });

  newTest({
    title: 'X deletes block',
    start: ['11111', '2|2222', '33333', '44444', '55555'],
    keysPressed: '<C-v>jjllX',
    end: ['11111', '2|2', '33', '44', '55555'],
  });

  newTest({
    title: 'Select register using " works in visual block mode',
    start: ['abcde', '0|1234', 'abcde', '01234'],
    keysPressed: '<C-v>llj"ayGo<C-r>a<Esc>',
    end: ['abcde', '01234', 'abcde', '01234', '123', 'bcd', '|'],
  });
}


async () => {
    await setupWorkspace();
    modeHandler = await getAndUpdateModeHandler();
  }


async () => {
    modeHandler.vimState.editor = vscode.window.activeTextEditor!;

    await modeHandler.handleKeyEvent('<C-v>');
    assert.strictEqual(modeHandler.currentMode, Mode.VisualBlock);

    await modeHandler.handleKeyEvent('<C-v>');
    assert.strictEqual(modeHandler.currentMode, Mode.Normal);
  }


() => {
    if (process.platform === 'darwin') {
      return;
    }

    test('<C-c> copies and sets mode to normal', async () => {
      await modeHandler.handleMultipleKeyEvents('ione two three'.split(''));
      await modeHandler.handleMultipleKeyEvents(['<Esc>', 'Y', 'p', 'p']);

      assertEqualLines(['one two three', 'one two three', 'one two three']);

      await modeHandler.handleMultipleKeyEvents(['<Esc>', 'H', '<C-v>', 'e', 'j', 'j', '<C-c>']);

      // ensuring we're back in normal
      assert.strictEqual(modeHandler.currentMode, Mode.Normal);

      // test copy by pasting back
      await modeHandler.handleMultipleKeyEvents(['H', '"', '+', 'P']);

      // TODO: should be
      // assertEqualLines(['oneone two three', 'oneone two three', 'oneone two three']);
      // unfortunately it is
      assertEqualLines(['one', 'one', 'one', 'one two three', 'one two three', 'one two three']);
    });
  }


async () => {
      await modeHandler.handleMultipleKeyEvents('ione two three'.split(''));
      await modeHandler.handleMultipleKeyEvents(['<Esc>', 'Y', 'p', 'p']);

      assertEqualLines(['one two three', 'one two three', 'one two three']);

      await modeHandler.handleMultipleKeyEvents(['<Esc>', 'H', '<C-v>', 'e', 'j', 'j', '<C-c>']);

      // ensuring we're back in normal
      assert.strictEqual(modeHandler.currentMode, Mode.Normal);

      // test copy by pasting back
      await modeHandler.handleMultipleKeyEvents(['H', '"', '+', 'P']);

      // TODO: should be
      // assertEqualLines(['oneone two three', 'oneone two three', 'oneone two three']);
      // unfortunately it is
      assertEqualLines(['one', 'one', 'one', 'one two three', 'one two three', 'one two three']);
    }


() => {
    this.props.dispatcher.refreshRepository(this.props.repository)
  }


() => {
    this.props.dispatcher.removeRepositories([this.props.repository], false)
  }


() => {
    this.props.dispatcher.relocateRepository(this.props.repository)
  }


async () => {
    const gitHubRepository = this.props.repository.gitHubRepository
    if (!gitHubRepository) {
      return
    }

    const cloneURL = gitHubRepository.cloneURL
    if (!cloneURL) {
      return
    }

    try {
      await this.props.dispatcher.cloneAgain(
        cloneURL,
        this.props.repository.path
      )
    } catch (error) {
      this.props.dispatcher.postError(error)
    }
  }


/** The view displayed when a repository is missing. */
export class MissingRepository extends React.Component<
  IMissingRepositoryProps,
  {}
> {
  public render() {
    const buttons = new Array<JSX.Element>()
    buttons.push(
      <Button key="locate" onClick={this.locate} type="submit">
        Locate…
      </Button>
    )

    if (this.canCloneAgain()) {
      buttons.push(
        <Button key="clone-again" onClick={this.cloneAgain}>
          Clone Again
        </Button>
      )
    }

    buttons.push(
      <Button key="remove" onClick={this.remove}>
        Remove
      </Button>
    )

    return (
      <UiView id="missing-repository-view">

Can't find "{this.props.repository.name}"

            It was last seen at{' '}
            {this.props.repository.path}.{' '}
            <LinkButton onClick={this.checkAgain}>Check again.</LinkButton>

        <Row>{buttons}</Row>
      </UiView>
    )
  }

  private canCloneAgain() {
    const gitHubRepository = this.props.repository.gitHubRepository
    return gitHubRepository && gitHubRepository.cloneURL
  }

  private checkAgain = () => {
    this.props.dispatcher.refreshRepository(this.props.repository)
  }

  private remove = () => {
    this.props.dispatcher.removeRepositories([this.props.repository], false)
  }

  private locate = () => {
    this.props.dispatcher.relocateRepository(this.props.repository)
  }

  private cloneAgain = async () => {
    const gitHubRepository = this.props.repository.gitHubRepository
    if (!gitHubRepository) {
      return
    }

    const cloneURL = gitHubRepository.cloneURL
    if (!cloneURL) {
      return
    }

    try {
      await this.props.dispatcher.cloneAgain(
        cloneURL,
        this.props.repository.path
      )
    } catch (error) {
      this.props.dispatcher.postError(error)
    }
  }
}


interface IMissingRepositoryProps {
  readonly dispatcher: Dispatcher
  readonly repository: Repository
}


public render() {
    const buttons = new Array<JSX.Element>()
    buttons.push(
      <Button key="locate" onClick={this.locate} type="submit">
        Locate…
      </Button>
    )

    if (this.canCloneAgain()) {
      buttons.push(
        <Button key="clone-again" onClick={this.cloneAgain}>
          Clone Again
        </Button>
      )
    }

    buttons.push(
      <Button key="remove" onClick={this.remove}>
        Remove
      </Button>
    )

    return (
      <UiView id="missing-repository-view">

Can't find "{this.props.repository.name}"

            It was last seen at{' '}
            {this.props.repository.path}.{' '}
            <LinkButton onClick={this.checkAgain}>Check again.</LinkButton>

        <Row>{buttons}</Row>
      </UiView>
    )
  }


private canCloneAgain() {
    const gitHubRepository = this.props.repository.gitHubRepository
    return gitHubRepository && gitHubRepository.cloneURL
  }


function calculateOptions (colors: (string | undefined)[] = [], legends: string[], labels: string[], values: (number | BN)[][]): State {
  const chartData = values.reduce((chartData, values, index): Config => {
    const color = colors[index] || alphaColor(COLORS[index]);
    const data = values.map((value): number => BN.isBN(value) ? value.toNumber() : value);

    chartData.datasets.push({
      backgroundColor: color,
      borderColor: color,
      data,
      fill: false,
      hoverBackgroundColor: color,
      label: legends[index]
    });

    return chartData;
  }, { datasets: [] as Dataset[], labels });

  return {
    chartData,
    chartOptions
  };
}


function LineChart ({ className, colors, labels, legends, values }: LineProps): React.ReactElement<LineProps> | null {
  const { chartData, chartOptions } = useMemo(
    () => calculateOptions(colors, legends, labels, values),
    [colors, labels, legends, values]
  );

  return (
    <div className={className}>
      <Chart.Line
        data={chartData}
        options={chartOptions}
      />
    </div>
  );
}


(hexColor: string): string =>
  // eslint-disable-next-line @typescript-eslint/no-unsafe-return,@typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access
  ChartJs.helpers.color(hexColor).alpha(0.65).rgbString()


(chartData, values, index): Config => {
    const color = colors[index] || alphaColor(COLORS[index]);
    const data = values.map((value): number => BN.isBN(value) ? value.toNumber() : value);

    chartData.datasets.push({
      backgroundColor: color,
      borderColor: color,
      data,
      fill: false,
      hoverBackgroundColor: color,
      label: legends[index]
    });

    return chartData;
  }


(value): number => BN.isBN(value) ? value.toNumber() : value


() => calculateOptions(colors, legends, labels, values)


interface State {
  chartData: ChartJs.ChartData;
  chartOptions: ChartJs.ChartOptions;
}


interface Dataset {
  data: number[];
  fill: boolean;
  label: string;
  backgroundColor: string;
  borderColor: string;
  hoverBackgroundColor: string;
}


interface Config {
  labels: string[];
  datasets: Dataset[];
}


export interface DataStoryContext {
  // Used to expose API routes as named HTTPRequest Nodes
  apis?: {
    name: string;
    url?: string;
  }[];
  // Used to expose data as named ResolveContextFeature Nodes
  models?: {
    [others: string]: unknown;
  };
  // We may put custom keys in the context
  [others: string]: unknown;
}


@Component()
export class BlocksService {
    constructor(
        @InjectRepository(Block)
        private readonly blockRepository: Repository<Block>
    ) { }

    async findAll(): Promise<ThinBlock[]> {
        return await this.blockRepository.find();
    }

    async find(id: number): Promise<Block> {
        return await this.blockRepository.findOne({ id: id });
    }

    async create(dto: BlockDto): Promise<Block> {
        const block = await this.blockRepository.create();
        block.updateFromDto(dto);
        const savedBlock = await this.blockRepository.save(block);
        return savedBlock;
    }

    async update(id: number, dto: BlockDto): Promise<Block> {
        const block = await this.blockRepository.findOne({ id: id });
        block.updateFromDto(dto);
        const savedBlock = await this.blockRepository.save(block);
        return savedBlock;
    }

    async delete(id: number): Promise<DeleteResult> {
        return await this.blockRepository.delete({ id });
    }
}


async findAll(): Promise<ThinBlock[]> {
        return await this.blockRepository.find();
    }


async find(id: number): Promise<Block> {
        return await this.blockRepository.findOne({ id: id });
    }


async create(dto: BlockDto): Promise<Block> {
        const block = await this.blockRepository.create();
        block.updateFromDto(dto);
        const savedBlock = await this.blockRepository.save(block);
        return savedBlock;
    }


async update(id: number, dto: BlockDto): Promise<Block> {
        const block = await this.blockRepository.findOne({ id: id });
        block.updateFromDto(dto);
        const savedBlock = await this.blockRepository.save(block);
        return savedBlock;
    }


async delete(id: number): Promise<DeleteResult> {
        return await this.blockRepository.delete({ id });
    }


({ children, id}) => {
  return <ContentContainer id = {id} className={styles.container}>{children}</ContentContainer>;
}


export type ContainerProps = {
  id?: string;
};


@Component({
  moduleId: module.id,
  selector: 'ripple-demo',
  templateUrl: 'ripple-demo.html',
  styleUrls: ['ripple-demo.scss'],
})
export class RippleDemoComponent {
  @ViewChild(MatRipple, {static: true}) ripple: MatRipple;

  centered = false;
  disabled = false;
  unbounded = false;
  rounded = false;
  radius: number = null;
  rippleSpeed = 1;
  rippleColor = '';

  disableButtonRipples = false;

  launchRipple(persistent = false) {
    if (this.ripple) {
      //this.ripple.launch(0, 0, { centered: true, persistent });
    }
  }

  fadeOutAll() {
    if (this.ripple) {
      //this.ripple.fadeOutAll();
    }
  }

}


launchRipple(persistent = false) {
    if (this.ripple) {
      //this.ripple.launch(0, 0, { centered: true, persistent });
    }
  }


fadeOutAll() {
    if (this.ripple) {
      //this.ripple.fadeOutAll();
    }
  }


() => {
        assert.step('foo-bar owner called');
      }


() => {
        assert.step('foo-baz owner called');
      }


class OwnerJitRuntimeResolver extends TestJitRuntimeResolver {
  lookupComponent(name: string, owner: () => void): ComponentDefinition | null {
    if (typeof owner === 'function') owner();

    return super.lookupComponent(name, owner);
  }

  lookupCompileTimeComponent(name: string, owner: () => void): CompileTimeComponent | null {
    if (typeof owner === 'function') owner();

    return super.lookupCompileTimeComponent(name, owner);
  }
}


class OwnerJitRenderDelegate extends JitRenderDelegate {
  protected resolver = new OwnerJitRuntimeResolver(this.registry);
}


class OwnerTest extends RenderTest {
  static suiteName = '[owner]';

  declare delegate: OwnerJitRenderDelegate;

  @test
  'owner can be used per-template in compile time resolver'(assert: Assert) {
    class FooBar extends EmberishCurlyComponent {
      layout = createTemplate('<FooBaz/>')(() => {
        assert.step('foo-bar owner called');
      });
    }

    class FooBaz extends EmberishCurlyComponent {
      layout = createTemplate('<FooQux/>')(() => {
        assert.step('foo-baz owner called');
      });
    }

    this.delegate.registerComponent('Curly', 'Curly', 'FooBar', null, FooBar);
    this.delegate.registerComponent('Curly', 'Curly', 'FooBaz', null, FooBaz);
    this.delegate.registerComponent('TemplateOnly', 'TemplateOnly', 'FooQux', 'testing');

    this.render('<FooBar/>');

    assert.verifySteps(['foo-bar owner called', 'foo-baz owner called'], 'owners used correctly');
  }

  @test
  'owner can be used per-template in runtime resolver'(assert: Assert) {
    class FooBar extends EmberishCurlyComponent {
      subcomponent = 'foo-baz';

      layout = createTemplate('{{component this.subcomponent}}')(() => {
        assert.step('foo-bar owner called');
      });
    }

    class FooBaz extends EmberishCurlyComponent {
      subcomponent = 'foo-qux';

      layout = createTemplate('{{component this.subcomponent}}')(() => {
        assert.step('foo-baz owner called');
      });
    }

    this.delegate.registerComponent('Curly', 'Curly', 'FooBar', null, FooBar);
    this.delegate.registerComponent('Curly', 'Curly', 'foo-baz', null, FooBaz);
    this.delegate.registerComponent('TemplateOnly', 'TemplateOnly', 'foo-qux', 'testing');

    this.render('<FooBar/>');

    assert.verifySteps(['foo-bar owner called', 'foo-baz owner called'], 'owners used correctly');
  }
}


class FooBar extends EmberishCurlyComponent {
      layout = createTemplate('<FooBaz/>')(() => {
        assert.step('foo-bar owner called');
      });
    }


class FooBaz extends EmberishCurlyComponent {
      layout = createTemplate('<FooQux/>')(() => {
        assert.step('foo-baz owner called');
      });
    }


class FooBar extends EmberishCurlyComponent {
      subcomponent = 'foo-baz';

      layout = createTemplate('{{component this.subcomponent}}')(() => {
        assert.step('foo-bar owner called');
      });
    }


class FooBaz extends EmberishCurlyComponent {
      subcomponent = 'foo-qux';

      layout = createTemplate('{{component this.subcomponent}}')(() => {
        assert.step('foo-baz owner called');
      });
    }


lookupComponent(name: string, owner: () => void): ComponentDefinition | null {
    if (typeof owner === 'function') owner();

    return super.lookupComponent(name, owner);
  }


lookupCompileTimeComponent(name: string, owner: () => void): CompileTimeComponent | null {
    if (typeof owner === 'function') owner();

    return super.lookupCompileTimeComponent(name, owner);
  }


@test
  'owner can be used per-template in compile time resolver'(assert: Assert) {
    class FooBar extends EmberishCurlyComponent {
      layout = createTemplate('<FooBaz/>')(() => {
        assert.step('foo-bar owner called');
      });
    }

    class FooBaz extends EmberishCurlyComponent {
      layout = createTemplate('<FooQux/>')(() => {
        assert.step('foo-baz owner called');
      });
    }

    this.delegate.registerComponent('Curly', 'Curly', 'FooBar', null, FooBar);
    this.delegate.registerComponent('Curly', 'Curly', 'FooBaz', null, FooBaz);
    this.delegate.registerComponent('TemplateOnly', 'TemplateOnly', 'FooQux', 'testing');

    this.render('<FooBar/>');

    assert.verifySteps(['foo-bar owner called', 'foo-baz owner called'], 'owners used correctly');
  }


@test
  'owner can be used per-template in runtime resolver'(assert: Assert) {
    class FooBar extends EmberishCurlyComponent {
      subcomponent = 'foo-baz';

      layout = createTemplate('{{component this.subcomponent}}')(() => {
        assert.step('foo-bar owner called');
      });
    }

    class FooBaz extends EmberishCurlyComponent {
      subcomponent = 'foo-qux';

      layout = createTemplate('{{component this.subcomponent}}')(() => {
        assert.step('foo-baz owner called');
      });
    }

    this.delegate.registerComponent('Curly', 'Curly', 'FooBar', null, FooBar);
    this.delegate.registerComponent('Curly', 'Curly', 'foo-baz', null, FooBaz);
    this.delegate.registerComponent('TemplateOnly', 'TemplateOnly', 'foo-qux', 'testing');

    this.render('<FooBar/>');

    assert.verifySteps(['foo-bar owner called', 'foo-baz owner called'], 'owners used correctly');
  }