Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions lib/interscript/equivalence.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# frozen_string_literal: true

module Interscript
# A3 (WO59): mechanical equivalence checking between two systems.
#
# Probes a sample set through both systems and reports the agreement
# ratio plus the minimal distinguishing samples. The sample set is
# caller-supplied or derived from the systems' own embedded tests.
class Equivalence
def self.check(first, second, samples:)
new(first, second).check(samples)
end

# Embedded test inputs of the given systems (the natural probe set
# for near-equivalent authorities).
def self.samples_from_tests(*codes)
codes.flat_map do |code|
Array(Interscript.parse(code).tests&.data).map(&:first).compact
end.uniq
end

def initialize(first, second)
@first = first
@second = second
end

def check(samples)
distinguishing = []
tested = 0
matched = 0
samples.each do |sample|
tested += 1
a = Interscript.transliterate(@first, sample)
b = Interscript.transliterate(@second, sample)
if a == b
matched += 1
else
distinguishing << { sample: sample, first: a, second: b }
end
end
ratio = tested.zero? ? 1.0 : matched.fdiv(tested)
{ tested: tested, matched: matched, ratio: ratio,
distinguishing: distinguishing }
end
end
end
72 changes: 72 additions & 0 deletions spec/interscript/equivalence_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# frozen_string_literal: true

require "json"
require "tmpdir"
require "interscript/equivalence"

RSpec.describe Interscript::Equivalence do
def write_map(dir, name, body)
path = File.join(dir, "#{name}.isc")
File.write(path, body)
path
end

def identical_pair(dir)
body = <<~ISC
system "TST:xx-Latn:Latn:1" {
metadata {
authority_id tst
id 1
language iso-639-2:xx
source_script Latn
destination_script Latn
}
tests { "ab" -> "ab" }
stage main { parallel { sub "a" "a" } }
}
ISC
[write_map(dir, "tst-a-1", body), write_map(dir, "tst-b-1", body.gsub("tst-a-1", "tst-b-1"))]
end

it "reports full agreement for identical systems" do
Dir.mktmpdir do |dir|
a, b = identical_pair(dir)
Interscript.load_path.unshift(dir)
begin
result = described_class.check("tst-a-1", "tst-b-1", samples: ["ab", "abc", "b"])
expect(result[:ratio]).to eq(1.0)
expect(result[:distinguishing]).to be_empty
ensure
Interscript.load_path.delete(dir)
end
end
end

it "finds the minimal distinguishing sample for divergent systems" do
Dir.mktmpdir do |dir|
body_a = <<~ISC
system "TST:xx-Latn:Latn:1" {
metadata {
authority_id tst
id 1
language iso-639-2:xx
source_script Latn
destination_script Latn
}
stage main { parallel { sub "a" "x" } }
}
ISC
body_b = body_a.gsub('sub "a" "x"', 'sub "a" "y"').gsub("id 1", "id 2")
write_map(dir, "tst-c-1", body_a)
write_map(dir, "tst-c-2", body_b)
Interscript.load_path.unshift(dir)
begin
result = described_class.check("tst-c-1", "tst-c-2", samples: ["a", "b"])
expect(result[:ratio]).to eq(0.5)
expect(result[:distinguishing]).to eq([{ sample: "a", first: "x", second: "y" }])
ensure
Interscript.load_path.delete(dir)
end
end
end
end
58 changes: 58 additions & 0 deletions spec/isc/semantics_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# frozen_string_literal: true

# WO80/A4 — normative rulings for the ISC semantic surface. Each vector
# pins the reference behavior; IS-1 v2 documents these as normative
# sections (the spec text accretes one section per ruled point).

require "tmpdir"

RSpec.describe "ISC semantics — parallel composition" do
def with_map(name, body)
dir = Dir.mktmpdir
File.write(File.join(dir, name), body)
Interscript.load_path.unshift(dir)
yield
ensure
Interscript.load_path.delete(dir) if dir
end

it "rules: a parallel block is single-pass longest-match" do
with_map("tst-long-1.isc", <<~ISC) do
system "TST:xx-Latn:Latn:1" {
metadata {
authority_id tst
id 1
language iso-639-2:xx
source_script Latn
destination_script Latn
}
stage main { parallel {
sub "b" "Y"
sub "ab" "X"
} }
}
ISC
expect(Interscript.transliterate("tst-long-1", "ab")).to eq("X")
end
end

it "rules: duplicate rules in a parallel resolve last-declared-wins" do
with_map("tst-tie-1.isc", <<~ISC) do
system "TST:xx-Latn:Latn:2" {
metadata {
authority_id tst
id 2
language iso-639-2:xx
source_script Latn
destination_script Latn
}
stage main { parallel {
sub "a" "1"
sub "a" "2"
} }
}
ISC
expect(Interscript.transliterate("tst-tie-1", "a")).to eq("2")
end
end
end
Loading