{"atom":"regression/range-endpoint-objects","family":"regression","slug":"range-endpoint-objects","header":{"atom":"regression/range-endpoint-objects","kind":"whole-program — a minimal repro, not yet reduced to an atom","source":"curated"},"axes":null,"cols":null,"engines":["rakudo-2026.07","rakupp-3.14.0"],"kinds":["ref","pp"],"defFrom":"rakupp:t/regression/range-endpoint-objects.raku","counts":{"a":1,"f":0,"d":0,"c":0,"r":0,"n":0},"files":["atoms/regression/range-endpoint-objects.grid"],"records":[["0001","","# Regression: a Range remembers the endpoint OBJECTS it was written with.\n# rakupp stored only the integers it iterates over, so `1/2 .. 1/3` gisted\n# `0.5..0.3333…` instead of `0.5..<1/3>` and `True .. False` came out `1..0`.\n# The objects ride in the otherwise-unused `ext`, so a plain `1..5` allocates\n# nothing; iteration still walks the numeric fields.\n# The rule is Rakudo's: a Real or undefined endpoint is kept as it is, a Str\n# (\"2\" -> 2) or a list (-> its element count) is NUMIFIED. `minmax` follows the\n# same rule, except that two Str extremes stay Strs.\n# Contract: exit 0 + last line PASS.\nmy @fail;\nsub check($got, $want, $what) { @fail.push(\"$what: got $got want $want\") unless $got eq $want }\n\n# kept as written\ncheck((1/2 .. 1/3).gist,   '0.5..<1/3>',              'rat-endpoints');\ncheck((True .. False).gist, 'Bool::True..Bool::False', 'bool-endpoints');\ncheck((1 .. 2.5).gist,     '1..2.5',                  'rat-upper');\ncheck((1 .. 1e0).gist,     '1..1e0',                  'num-upper');\ncheck((1 .. Any).gist,     '1..Any',                  'undefined-upper');\ncheck((1.5 ..^ 3.5).gist,  '1.5..^3.5',               'fractional-exclusive');\n\n# numified, as Rakudo does\ncheck((1 .. \"2\").gist,     '1..2',                    'str-endpoint-numifies');\ncheck(((1, 2) .. (3, 4, 5)).gist, '2..3',             'list-endpoint-numifies');\n\n# plain integer ranges are untouched\ncheck((1 .. 5).gist,       '1..5',                    'int-range');\ncheck((1 ^..^ 5).gist,     '1^..^5',                  'int-range-exclusive');\ncheck((\"a\" .. \"e\").gist,   '\"a\"..\"e\"',                'str-range');\n\n# .min/.max/.bounds answer the objects, not the integers iterated over\ncheck((1/2 .. 1/3).min.^name, 'Rat', 'min-type');\ncheck((1/2 .. 1/3).max.raku,  '<1/3>', 'max-value');\ncheck((1/2 .. 1/3).bounds.raku, '(0.5, <1/3>)', 'bounds');\ncheck((\"a\" .. \"e\").bounds.raku, '(\"a\", \"e\")', 'str-bounds');\ncheck((1 .. 5).min.^name, 'Int', 'int-min-type');\n\n# iteration is unaffected — and a Rat endpoint yields Rats, not Nums\ncheck((1 .. 5).list.raku,     '(1, 2, 3, 4, 5)', 'int-iteration');\ncheck((1 ..^ 5).list.raku,    '(1, 2, 3, 4)',    'int-exclusive-iteration');\ncheck((1.5 .. 3.5).list.raku, '(1.5, 2.5, 3.5)', 'rat-iteration');\ncheck((\"a\" .. \"e\").list.raku, '(\"a\", \"b\", \"c\", \"d\", \"e\")', 'str-iteration');\ncheck((1 .. 5).sum,           '15',              'sum');\ncheck(([+] (1 .. 100)),       '5050',            'reduce-over-range');\n\n# minmax spans the extreme ELEMENTS\ncheck((\"a\" minmax \"b\").gist,  '\"a\"..\"b\"',                'minmax-str');\ncheck((True minmax False).gist, 'Bool::False..Bool::True', 'minmax-bool');\ncheck((1/2 minmax 1/3).gist,  '<1/3>..0.5',              'minmax-rat');\ncheck((3 minmax 1).gist,      '1..3',                    'minmax-int');\ncheck((1 minmax \"2\").gist,    '1..2',                    'minmax-mixed-numifies');\ncheck((1 minmax Any).gist,    '1..1',                    'minmax-skips-undefined');\n\nif @fail { note \"FAILED: @fail.join('; ')\"; say 'FAIL' } else { say 'PASS' }","output PASS","a",["PASS","PASS"],0,[["budget","20s"]]]]}