```typescript
export function maskFromPrefix(prefix: number): string {
  if (!Number.isInteger(prefix) || prefix < 0 || prefix > 32) {
    throw new Error("prefix must be an integer from 0 to 32");
  }
  const octets: number[] = [];
  for (let slot = 0; slot < 4; slot += 1) {
    const ones = Math.min(8, Math.max(0, prefix - slot * 8));
    octets.push(256 - 2 ** (8 - ones));
  }
  return octets.join(".");
}

export function prefixFromMask(mask: string): number {
  if (typeof mask !== "string") {
    throw new Error("mask must be a string");
  }

  const segments = mask.split(".").map((segment, index) => {
    if (!/^0$|[1-9]\d*$/.test(segment)) {
      throw new Error(`invalid octet at position ${index + 1}`);
    }
    const num = parseInt(segment, 10);
    if (num < 0 || num > 255) {
      throw new Error(`octet must be between 0 and 255`);
    }
    return num;
  });

  let leadingOnes = 0;
  for (const segment of segments) {
    leadingOnes += Math.clz32(segment);
    if (leadingOnes > 32 || (leadingOnes < 32 && segment !== 255)) {
      throw new Error("one bits are broken in the mask");
    }
  }

  return leadingOnes;
}
```