import assert from 'node:assert/strict';
import { readFile } from 'node:fs/promises';
import { resolve } from 'node:path';

const evidenceRoot = resolve(new URL('.', import.meta.url).pathname);
const record = JSON.parse(await readFile(resolve(evidenceRoot, 'bitcoinos-trust-model.json'), 'utf8'));

function popcount(value) {
  let count = 0;
  for (let current = value; current; current >>>= 1) count += current & 1;
  return count;
}

function choose(n, k) {
  const smaller = Math.min(k, n - k);
  let result = 1n;
  for (let i = 1; i <= smaller; i += 1) {
    result = (result * BigInt(n - smaller + i)) / BigInt(i);
  }
  return Number(result);
}

function enumerateOptimisticSafety(verifierCount, releasesWithoutEffectiveChallenge) {
  assert.ok(Number.isInteger(verifierCount) && verifierCount > 0 && verifierCount <= 20);
  const totalEffectiveChallengeStates = 2 ** verifierCount;
  let invalidReleaseStates = 0;
  let invalidReleaseBlockedStates = 0;

  // A set bit is an assumed event: that verifier's complete conflict/dispute
  // sequence became canonical before every competing timeout. The release
  // rule is also an input. This is an aggregate truth-table enumeration, not
  // an exploration of BitSNARK's transaction state machine or a probability
  // model. The published v0.2 implementation has one verifier; counts above
  // one are hypothetical one-of-N generalizations.
  for (let mask = 0; mask < totalEffectiveChallengeStates; mask += 1) {
    const hasEffectiveChallenge = mask !== 0;
    const invalidRelease = releasesWithoutEffectiveChallenge && !hasEffectiveChallenge;
    if (invalidRelease) invalidReleaseStates += 1;
    else invalidReleaseBlockedStates += 1;
  }

  return {
    verifierCount,
    totalEffectiveChallengeStates,
    invalidReleaseStates,
    invalidReleaseBlockedStates,
    unconditionalInvalidProofSafety: invalidReleaseStates === 0,
    counterexampleEffectiveChallengeMask: invalidReleaseStates ? 0 : null
  };
}

function enumerateThreshold({ participantCount, threshold, destinationConstrainedByScript, proofRequiredByScript }) {
  assert.ok(Number.isInteger(participantCount) && participantCount > 0 && participantCount <= 20);
  assert.ok(Number.isInteger(threshold) && threshold > 0 && threshold <= participantCount);
  const totalCoalitions = 2 ** participantCount;
  let authorizingCoalitions = 0;
  let arbitraryDestinationCoalitions = 0;
  let minimumAuthorizingCoalition = null;

  for (let mask = 0; mask < totalCoalitions; mask += 1) {
    const size = popcount(mask);
    if (size < threshold) continue;
    authorizingCoalitions += 1;
    if (minimumAuthorizingCoalition === null || size < minimumAuthorizingCoalition) {
      minimumAuthorizingCoalition = size;
    }
    if (!destinationConstrainedByScript && !proofRequiredByScript) arbitraryDestinationCoalitions += 1;
  }

  return {
    participantCount,
    threshold,
    totalCoalitions,
    authorizingCoalitions,
    minimumAuthorizingCoalition,
    arbitraryDestinationCoalitions,
    universalNoCoalitionCanRedirect: arbitraryDestinationCoalitions === 0,
    minimumSetupSignersWithholdingToPreventPresigning: participantCount - threshold + 1,
    combinatorialCrossCheck: Array.from(
      { length: participantCount - threshold + 1 },
      (_, offset) => choose(participantCount, threshold + offset)
    ).reduce((sum, value) => sum + value, 0)
  };
}

function isInclusionMinimal(mask, predicate, totalAuthorities) {
  for (let index = 0; index < totalAuthorities; index += 1) {
    const bit = 2 ** index;
    if ((mask & bit) !== 0 && predicate(mask & ~bit)) return false;
  }
  return true;
}

function minimumPairwiseIntersection(coalitions) {
  assert.ok(coalitions.length > 0);
  if (coalitions.length === 1) return popcount(coalitions[0]);
  let minimum = Infinity;
  for (let left = 0; left < coalitions.length; left += 1) {
    for (let right = left + 1; right < coalitions.length; right += 1) {
      minimum = Math.min(minimum, popcount(coalitions[left] & coalitions[right]));
      if (minimum === 0) return 0;
    }
  }
  return minimum;
}

function enumerateAuthorityPolicy(totalAuthorities, authorizes) {
  assert.ok(Number.isInteger(totalAuthorities) && totalAuthorities > 0 && totalAuthorities <= 20);
  const totalCoalitions = 2 ** totalAuthorities;
  const fullMask = totalCoalitions - 1;
  const authorizingMasks = [];
  const authorizingBySize = Array(totalAuthorities + 1).fill(0);
  const freezingBySize = Array(totalAuthorities + 1).fill(0);
  const minimalAuthorizingBySize = Array(totalAuthorities + 1).fill(0);
  const minimalRefusalBySize = Array(totalAuthorities + 1).fill(0);
  let minimalAuthorizingCoalitions = 0;
  let minimalRefusalCoalitions = 0;
  let minimumAuthorizingCoalition = null;
  let minimumRefusalCoalition = null;

  const freezes = (refusalMask) => !authorizes(fullMask ^ refusalMask);

  for (let mask = 0; mask < totalCoalitions; mask += 1) {
    const size = popcount(mask);
    if (authorizes(mask)) {
      authorizingMasks.push(mask);
      authorizingBySize[size] += 1;
      if (minimumAuthorizingCoalition === null || size < minimumAuthorizingCoalition) {
        minimumAuthorizingCoalition = size;
      }
      if (isInclusionMinimal(mask, authorizes, totalAuthorities)) {
        minimalAuthorizingCoalitions += 1;
        minimalAuthorizingBySize[size] += 1;
      }
    }
    if (freezes(mask)) {
      freezingBySize[size] += 1;
      if (minimumRefusalCoalition === null || size < minimumRefusalCoalition) {
        minimumRefusalCoalition = size;
      }
      if (isInclusionMinimal(mask, freezes, totalAuthorities)) {
        minimalRefusalCoalitions += 1;
        minimalRefusalBySize[size] += 1;
      }
    }
  }

  const histogramRows = (histogram) => histogram.flatMap((count, coalitionSize) => (
    count === 0 ? [] : [{ coalitionSize, count }]
  ));

  return {
    totalCoalitions,
    authorizingCoalitions: authorizingMasks.length,
    minimumAuthorizingCoalition,
    minimalAuthorizingCoalitions,
    minimalAuthorizingCoalitionsBySize: histogramRows(minimalAuthorizingBySize),
    authorizingCoalitionsBySize: histogramRows(authorizingBySize),
    freezingRefusalCoalitions: freezingBySize.reduce((sum, count) => sum + count, 0),
    minimumRefusalCoalition,
    minimalRefusalCoalitions,
    minimalRefusalCoalitionsBySize: histogramRows(minimalRefusalBySize),
    freezingRefusalCoalitionsBySize: histogramRows(freezingBySize),
    minimumAuthorizingQuorumIntersection: minimumPairwiseIntersection(authorizingMasks)
  };
}

function enumerateLaterGrailProThreshold(input) {
  const {
    comparisonScope,
    participantCount,
    threshold,
    coalitionUnit,
    humanCountInferencePermitted,
    salePeriodArchitectureInferencePermitted,
    custodianVetoVariants
  } = input;

  assert.equal(comparisonScope, 'later-documented-example-only');
  assert.equal(humanCountInferencePermitted, false);
  assert.equal(salePeriodArchitectureInferencePermitted, false);
  assert.match(coalitionUnit, /TEE\/policy compromise/);
  assert.equal(input.destinationConstrainedByScript, false);
  assert.equal(input.proofRequiredByScript, false);
  assert.ok(input.scopeWarning.includes('not a finding about the original sale-period architecture'));
  assert.ok(input.scopeWarning.includes('count of human persons'));

  const base = enumerateAuthorityPolicy(
    participantCount,
    (mask) => popcount(mask) >= threshold
  );
  const firstFreezingSize = participantCount - threshold + 1;
  const combinatorialCrossChecks = {
    authorizingAuthorityCoalitions: Array.from(
      { length: participantCount - threshold + 1 },
      (_, offset) => choose(participantCount, threshold + offset)
    ).reduce((sum, count) => sum + count, 0),
    minimalAuthorizingAuthorityCoalitions: choose(participantCount, threshold),
    freezingRefusalCoalitions: Array.from(
      { length: participantCount - firstFreezingSize + 1 },
      (_, offset) => choose(participantCount, firstFreezingSize + offset)
    ).reduce((sum, count) => sum + count, 0),
    minimalRefusalCoalitions: choose(participantCount, firstFreezingSize)
  };

  const distinct = custodianVetoVariants.distinctMandatoryAuthority;
  assert.equal(distinct.totalAuthorities, participantCount + 1);
  assert.equal(distinct.custodianAuthorityIndex, participantCount);
  assert.equal(distinct.custodianCountsTowardOperatorThreshold, false);
  const distinctCustodianBit = 2 ** distinct.custodianAuthorityIndex;
  const operatorMask = (2 ** participantCount) - 1;
  const distinctResult = enumerateAuthorityPolicy(
    distinct.totalAuthorities,
    (mask) => (mask & distinctCustodianBit) !== 0 && popcount(mask & operatorMask) >= threshold
  );

  const embedded = custodianVetoVariants.mandatoryAuthorityWithinOperatorSet;
  assert.equal(embedded.totalAuthorities, participantCount);
  assert.ok(embedded.custodianAuthorityIndex >= 0 && embedded.custodianAuthorityIndex < participantCount);
  assert.equal(embedded.custodianCountsTowardOperatorThreshold, true);
  const embeddedCustodianBit = 2 ** embedded.custodianAuthorityIndex;
  const embeddedResult = enumerateAuthorityPolicy(
    embedded.totalAuthorities,
    (mask) => (mask & embeddedCustodianBit) !== 0 && popcount(mask) >= threshold
  );

  const summarizeCustodianVariant = (scope, totalAuthorities, result) => ({
    scope,
    totalAuthorities,
    totalCoalitions: result.totalCoalitions,
    authorizingCoalitions: result.authorizingCoalitions,
    minimumAuthorizingCoalition: result.minimumAuthorizingCoalition,
    minimalAuthorizingCoalitions: result.minimalAuthorizingCoalitions,
    minimalAuthorizingCoalitionsBySize: result.minimalAuthorizingCoalitionsBySize,
    freezingRefusalCoalitions: result.freezingRefusalCoalitions,
    minimumRefusalCoalition: result.minimumRefusalCoalition,
    minimalRefusalCoalitions: result.minimalRefusalCoalitions,
    minimalRefusalCoalitionsBySize: result.minimalRefusalCoalitionsBySize,
    safetyToleranceCompromisedAuthorities: result.minimumAuthorizingCoalition - 1,
    livenessToleranceUnavailableAuthorities: result.minimumRefusalCoalition - 1,
    minimumAuthorizingQuorumIntersection: result.minimumAuthorizingQuorumIntersection
  });

  return {
    comparisonScope,
    coalitionUnit: 'effective-signing-authority',
    participantCount,
    threshold,
    totalAuthorityCoalitions: base.totalCoalitions,
    authorizingAuthorityCoalitions: base.authorizingCoalitions,
    minimumAuthorizingAuthorityCoalition: base.minimumAuthorizingCoalition,
    minimalAuthorizingAuthorityCoalitions: base.minimalAuthorizingCoalitions,
    minimalAuthorizingAuthorityCoalitionsBySize: base.minimalAuthorizingCoalitionsBySize,
    authorizingAuthorityCoalitionsBySize: base.authorizingCoalitionsBySize,
    freezingRefusalCoalitions: base.freezingRefusalCoalitions,
    minimumRefusalCoalition: base.minimumRefusalCoalition,
    minimalRefusalCoalitions: base.minimalRefusalCoalitions,
    minimalRefusalCoalitionsBySize: base.minimalRefusalCoalitionsBySize,
    freezingRefusalCoalitionsBySize: base.freezingRefusalCoalitionsBySize,
    safetyToleranceCompromisedAuthorities: base.minimumAuthorizingCoalition - 1,
    livenessToleranceUnavailableAuthorities: base.minimumRefusalCoalition - 1,
    simultaneousSafetyAndLivenessTolerance: Math.min(
      base.minimumAuthorizingCoalition - 1,
      base.minimumRefusalCoalition - 1
    ),
    minimumQuorumIntersection: base.minimumAuthorizingQuorumIntersection,
    combinatorialCrossChecks,
    custodianVetoVariants: {
      distinctMandatoryAuthority: summarizeCustodianVariant(
        distinct.description,
        distinct.totalAuthorities,
        distinctResult
      ),
      mandatoryAuthorityWithinOperatorSet: summarizeCustodianVariant(
        embedded.description,
        embedded.totalAuthorities,
        embeddedResult
      )
    }
  };
}

assert.equal(record.researchCutoff, '2026-08-21');
assert.equal(record.scope.productionSystemClaimed, false);
assert.equal(record.modelInputs.optimisticRelease.invalidProof, true);
assert.equal(record.modelInputs.optimisticRelease.releasesWithoutEffectiveChallenge, true);
assert.equal(record.modelInputs.optimisticRelease.implementedVerifierCount, 1);
assert.ok(record.modelInputs.optimisticRelease.generalizationWarning.includes('hypothetical'));
assert.ok(record.modelInputs.optimisticRelease.effectiveChallengeEvent.includes('becomes canonical'));
assert.ok(record.modelInputs.optimisticRelease.effectiveChallengeEnablingPremises.length >= 6);
assert.equal(record.modelInputs.optimisticRelease.fixedReleasePremises.length, 4);
assert.ok(record.modelInputs.optimisticRelease.formalRule.includes('ConfirmedToClaimant'));

const optimistic = record.modelInputs.optimisticRelease.verifierCounts.map((count) => (
  enumerateOptimisticSafety(count, record.modelInputs.optimisticRelease.releasesWithoutEffectiveChallenge)
));
assert.deepEqual(optimistic, record.expectedResults.optimisticRelease);

const publishedLockedFunds = enumerateThreshold(record.modelInputs.publishedLockedFunds);
assert.deepEqual(publishedLockedFunds, record.expectedResults.publishedLockedFunds);
assert.equal(publishedLockedFunds.universalNoCoalitionCanRedirect, false);
assert.equal(publishedLockedFunds.minimumAuthorizingCoalition, 2);

assert.match(record.definitions.laterGrailProAuthorityCompromise, /TEE-held key/);
assert.match(record.definitions.laterGrailProPersonCountBoundary, /rather than people/);
const laterGrailPro = enumerateLaterGrailProThreshold(record.modelInputs.laterGrailProThreshold);
assert.deepEqual(laterGrailPro, record.expectedResults.laterGrailProThreshold);
assert.equal(
  laterGrailPro.authorizingAuthorityCoalitions,
  laterGrailPro.combinatorialCrossChecks.authorizingAuthorityCoalitions
);
assert.equal(laterGrailPro.minimumAuthorizingAuthorityCoalition, 12);
assert.equal(laterGrailPro.minimalAuthorizingAuthorityCoalitions, 1820);
assert.equal(laterGrailPro.freezingRefusalCoalitions, 63019);
assert.equal(laterGrailPro.minimumRefusalCoalition, 5);
assert.equal(laterGrailPro.minimalRefusalCoalitions, 4368);
assert.equal(laterGrailPro.safetyToleranceCompromisedAuthorities, 11);
assert.equal(laterGrailPro.livenessToleranceUnavailableAuthorities, 4);
assert.equal(laterGrailPro.simultaneousSafetyAndLivenessTolerance, 4);
assert.equal(laterGrailPro.minimumQuorumIntersection, 8);
const distinctCustodian = laterGrailPro.custodianVetoVariants.distinctMandatoryAuthority;
assert.equal(distinctCustodian.authorizingCoalitions, 2517);
assert.equal(distinctCustodian.minimumAuthorizingCoalition, 13);
assert.equal(distinctCustodian.minimalAuthorizingCoalitions, 1820);
assert.equal(distinctCustodian.freezingRefusalCoalitions, 128555);
assert.equal(distinctCustodian.minimumRefusalCoalition, 1);
assert.equal(distinctCustodian.minimalRefusalCoalitions, 4369);
assert.deepEqual(distinctCustodian.minimalRefusalCoalitionsBySize, [
  { coalitionSize: 1, count: 1 },
  { coalitionSize: 5, count: 4368 }
]);
assert.equal(distinctCustodian.safetyToleranceCompromisedAuthorities, 12);
assert.equal(distinctCustodian.livenessToleranceUnavailableAuthorities, 0);
assert.equal(distinctCustodian.minimumAuthorizingQuorumIntersection, 9);
const embeddedCustodian = laterGrailPro.custodianVetoVariants.mandatoryAuthorityWithinOperatorSet;
assert.equal(embeddedCustodian.authorizingCoalitions, 1941);
assert.equal(embeddedCustodian.minimumAuthorizingCoalition, 12);
assert.equal(embeddedCustodian.minimalAuthorizingCoalitions, 1365);
assert.equal(embeddedCustodian.freezingRefusalCoalitions, 63595);
assert.equal(embeddedCustodian.minimumRefusalCoalition, 1);
assert.equal(embeddedCustodian.minimalRefusalCoalitions, 3004);
assert.deepEqual(embeddedCustodian.minimalRefusalCoalitionsBySize, [
  { coalitionSize: 1, count: 1 },
  { coalitionSize: 5, count: 3003 }
]);
assert.equal(embeddedCustodian.safetyToleranceCompromisedAuthorities, 11);
assert.equal(embeddedCustodian.livenessToleranceUnavailableAuthorities, 0);
assert.equal(embeddedCustodian.minimumAuthorizingQuorumIntersection, 8);

// The proposition text, source facts, execution records, TLA+ trace, and legal
// boundaries are documentary inputs outside this executable's scope. This
// file checks only the finite Boolean and threshold arithmetic recorded above.
assert.equal(new Set(record.propositions.map(({ id }) => id)).size, record.propositions.length);

const output = {
  arithmeticChecksPassed: true,
  verificationScope: 'finite Boolean and threshold arithmetic only',
  record: 'bitcoinos-trust-model.json',
  optimisticRelease: optimistic,
  publishedLockedFunds,
  laterGrailProThreshold: laterGrailPro,
  conclusion: record.technicalConclusion
};

if (process.argv.includes('--json')) process.stdout.write(`${JSON.stringify(output, null, 2)}\n`);
else {
  process.stdout.write('BitcoinOS finite-model arithmetic PASS\n');
  process.stdout.write('- scope: finite Boolean and threshold arithmetic; source, execution, TLA+, and legal records are not authenticated by this script\n');
  process.stdout.write(`- assumed no-effective-challenge release rule has a counterexample state: ${optimistic.every((row) => row.invalidReleaseStates === 1) ? 'yes' : 'no'}\n`);
  process.stdout.write(`- published Tapscript-leaf minimum authorizing coalition: ${publishedLockedFunds.minimumAuthorizingCoalition}-of-${publishedLockedFunds.participantCount}\n`);
  process.stdout.write(`- later comparison only; coalition unit: ${laterGrailPro.coalitionUnit} after TEE/policy compromise (not people or sale-period architecture)\n`);
  process.stdout.write(`- later 12-of-16 authority coalition universe: ${laterGrailPro.totalAuthorityCoalitions}\n`);
  process.stdout.write(`- later 12-of-16 authorizing authority coalitions: ${laterGrailPro.authorizingAuthorityCoalitions}; minimal: ${laterGrailPro.minimalAuthorizingAuthorityCoalitions} of size ${laterGrailPro.minimumAuthorizingAuthorityCoalition}\n`);
  process.stdout.write(`- later 12-of-16 freezing refusal coalitions: ${laterGrailPro.freezingRefusalCoalitions}; minimal: ${laterGrailPro.minimalRefusalCoalitions} of size ${laterGrailPro.minimumRefusalCoalition}\n`);
  process.stdout.write(`- later 12-of-16 safety/liveness tolerances: ${laterGrailPro.safetyToleranceCompromisedAuthorities} compromised / ${laterGrailPro.livenessToleranceUnavailableAuthorities} unavailable; simultaneous: ${laterGrailPro.simultaneousSafetyAndLivenessTolerance}\n`);
  process.stdout.write(`- later 12-of-16 minimum quorum intersection: ${laterGrailPro.minimumQuorumIntersection}\n`);
  process.stdout.write(`- distinct mandatory custodian veto: ${distinctCustodian.authorizingCoalitions} authorizing (${distinctCustodian.minimalAuthorizingCoalitions} minimal, size ${distinctCustodian.minimumAuthorizingCoalition}); ${distinctCustodian.freezingRefusalCoalitions} freezing, minimum size ${distinctCustodian.minimumRefusalCoalition}, with ${distinctCustodian.minimalRefusalCoalitions} inclusion-minimal (1 of size 1; 4368 of size 5)\n`);
  process.stdout.write(`- embedded mandatory custodian veto: ${embeddedCustodian.authorizingCoalitions} authorizing (${embeddedCustodian.minimalAuthorizingCoalitions} minimal, size ${embeddedCustodian.minimumAuthorizingCoalition}); ${embeddedCustodian.freezingRefusalCoalitions} freezing, minimum size ${embeddedCustodian.minimumRefusalCoalition}, with ${embeddedCustodian.minimalRefusalCoalitions} inclusion-minimal (1 of size 1; 3003 of size 5)\n`);
  process.stdout.write(`- conclusion: ${record.technicalConclusion}\n`);
}
